diff options
author | Parnell Springmeyer <parnell@digitalmentat.com> | 2017-01-25 11:08:05 -0800 |
---|---|---|
committer | Parnell Springmeyer <parnell@digitalmentat.com> | 2017-01-25 11:08:05 -0800 |
commit | bae00e8aa8f3faff90e28e19cd5074b8c26d0d0e (patch) | |
tree | 56abaf30f11ad2f24b9fb7729f74c5fff50fbd93 /nixos/modules/services | |
parent | 1f9494b752082ec3ac048e56d1c6364a2e23a675 (diff) | |
parent | 104c3db6594043dbb81005303f055b02145305a5 (diff) | |
download | nixpkgs-bae00e8aa8f3faff90e28e19cd5074b8c26d0d0e.tar nixpkgs-bae00e8aa8f3faff90e28e19cd5074b8c26d0d0e.tar.gz nixpkgs-bae00e8aa8f3faff90e28e19cd5074b8c26d0d0e.tar.bz2 nixpkgs-bae00e8aa8f3faff90e28e19cd5074b8c26d0d0e.tar.lz nixpkgs-bae00e8aa8f3faff90e28e19cd5074b8c26d0d0e.tar.xz nixpkgs-bae00e8aa8f3faff90e28e19cd5074b8c26d0d0e.tar.zst nixpkgs-bae00e8aa8f3faff90e28e19cd5074b8c26d0d0e.zip |
setcap-wrapper: Merging with upstream master and resolving conflicts
Diffstat (limited to 'nixos/modules/services')
247 files changed, 8979 insertions, 2518 deletions
diff --git a/nixos/modules/services/audio/alsa.nix b/nixos/modules/services/audio/alsa.nix index c63f4dc8d7f..53786dbc627 100644 --- a/nixos/modules/services/audio/alsa.nix +++ b/nixos/modules/services/audio/alsa.nix @@ -33,16 +33,6 @@ in ''; }; - enableMediaKeys = mkOption { - type = types.bool; - default = false; - description = '' - Whether to enable volume and capture control with keyboard media keys. - - Enabling this will turn on <option>services.actkbd</option>. - ''; - }; - extraConfig = mkOption { type = types.lines; default = ""; @@ -54,6 +44,31 @@ in ''; }; + mediaKeys = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable volume and capture control with keyboard media keys. + + Enabling this will turn on <option>services.actkbd</option>. + ''; + }; + + volumeStep = mkOption { + type = types.string; + default = "1"; + example = "1%"; + description = '' + The value by which to increment/decrement volume on media keys. + + See amixer(1) for allowed values. + ''; + }; + + }; + }; }; @@ -90,17 +105,17 @@ in }; }; - services.actkbd = mkIf config.sound.enableMediaKeys { + services.actkbd = mkIf config.sound.mediaKeys.enable { enable = true; bindings = [ # "Mute" media key { keys = [ 113 ]; events = [ "key" ]; command = "${alsaUtils}/bin/amixer -q set Master toggle"; } # "Lower Volume" media key - { keys = [ 114 ]; events = [ "key" "rep" ]; command = "${alsaUtils}/bin/amixer -q set Master 1- unmute"; } + { keys = [ 114 ]; events = [ "key" "rep" ]; command = "${alsaUtils}/bin/amixer -q set Master ${config.sound.mediaKeys.volumeStep}- unmute"; } # "Raise Volume" media key - { keys = [ 115 ]; events = [ "key" "rep" ]; command = "${alsaUtils}/bin/amixer -q set Master 1+ unmute"; } + { keys = [ 115 ]; events = [ "key" "rep" ]; command = "${alsaUtils}/bin/amixer -q set Master ${config.sound.mediaKeys.volumeStep}+ unmute"; } # "Mic Mute" media key { keys = [ 190 ]; events = [ "key" ]; command = "${alsaUtils}/bin/amixer -q set Capture toggle"; } diff --git a/nixos/modules/services/audio/mopidy.nix b/nixos/modules/services/audio/mopidy.nix index 029b14ab472..c0a0f037429 100644 --- a/nixos/modules/services/audio/mopidy.nix +++ b/nixos/modules/services/audio/mopidy.nix @@ -21,13 +21,7 @@ in { services.mopidy = { - enable = mkOption { - default = false; - type = types.bool; - description = '' - Whether to enable Mopidy, a music player daemon. - ''; - }; + enable = mkEnableOption "Mopidy, a music player daemon"; dataDir = mkOption { default = "/var/lib/mopidy"; diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix index 5d5fef66794..a89215d7382 100644 --- a/nixos/modules/services/audio/mpd.nix +++ b/nixos/modules/services/audio/mpd.nix @@ -33,6 +33,7 @@ in { services.mpd = { enable = mkOption { + type = types.bool; default = false; description = '' Whether to enable MPD, the music player daemon. @@ -40,6 +41,7 @@ in { }; musicDirectory = mkOption { + type = types.path; default = "${cfg.dataDir}/music"; description = '' The directory where mpd reads music from. @@ -47,6 +49,7 @@ in { }; extraConfig = mkOption { + type = types.lines; default = ""; description = '' Extra directives added to to the end of MPD's configuration file, @@ -56,6 +59,7 @@ in { }; dataDir = mkOption { + type = types.path; default = "/var/lib/mpd"; description = '' The directory where MPD stores its state, tag cache, @@ -64,11 +68,13 @@ in { }; user = mkOption { + type = types.str; default = "mpd"; description = "User account under which MPD runs."; }; group = mkOption { + type = types.str; default = "mpd"; description = "Group account under which MPD runs."; }; @@ -76,15 +82,17 @@ in { network = { listenAddress = mkOption { - default = "any"; + type = types.str; + default = "127.0.0.1"; + example = "any"; description = '' - This setting sets the address for the daemon to listen on. Careful attention - should be paid if this is assigned to anything other then the default, any. - This setting can deny access to control of the daemon. + The address for the daemon to listen on. + Use <literal>any</literal> to listen on all addresses. ''; }; port = mkOption { + type = types.int; default = 6600; description = '' This setting is the TCP port that is desired for the daemon to get assigned @@ -114,12 +122,12 @@ in { after = [ "network.target" "sound.target" ]; description = "Music Player Daemon"; wantedBy = [ "multi-user.target" ]; - path = [ pkgs.mpd ]; + preStart = "mkdir -p ${cfg.dataDir} && chown -R ${cfg.user}:${cfg.group} ${cfg.dataDir}"; - script = "exec mpd --no-daemon ${mpdConf}"; serviceConfig = { User = "${cfg.user}"; PermissionsStartOnly = true; + ExecStart = "${pkgs.mpd}/bin/mpd --no-daemon ${mpdConf}"; }; }; diff --git a/nixos/modules/services/audio/ympd.nix b/nixos/modules/services/audio/ympd.nix new file mode 100644 index 00000000000..d34c1c9d83c --- /dev/null +++ b/nixos/modules/services/audio/ympd.nix @@ -0,0 +1,57 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.ympd; +in { + + ###### interface + + options = { + + services.ympd = { + + enable = mkEnableOption "ympd, the MPD Web GUI"; + + webPort = mkOption { + type = types.string; + default = "8080"; + description = "The port where ympd's web interface will be available."; + example = "ssl://8080:/path/to/ssl-private-key.pem"; + }; + + mpd = { + host = mkOption { + type = types.string; + default = "localhost"; + description = "The host where MPD is listening."; + example = "localhost"; + }; + + port = mkOption { + type = types.int; + default = config.services.mpd.network.port; + description = "The port where MPD is listening."; + example = 6600; + }; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + systemd.services.ympd = { + description = "Standalone MPD Web GUI written in C"; + wantedBy = [ "multi-user.target" ]; + serviceConfig.ExecStart = "${pkgs.ympd}/bin/ympd --host ${cfg.mpd.host} --port ${toString cfg.mpd.port} --webport ${cfg.webPort} --user nobody"; + }; + + }; + +} diff --git a/nixos/modules/services/backup/bacula.nix b/nixos/modules/services/backup/bacula.nix index 8a26aae75fe..340b0cf0723 100644 --- a/nixos/modules/services/backup/bacula.nix +++ b/nixos/modules/services/backup/bacula.nix @@ -198,8 +198,7 @@ in { description = '' This option defines director resources in Bacula File Daemon. ''; - type = types.attrsOf types.optionSet; - options = [ directorOptions ]; + type = with types; attrsOf (submodule directorOptions); }; extraClientConfig = mkOption { @@ -253,8 +252,7 @@ in { description = '' This option defines Director resources in Bacula Storage Daemon. ''; - type = types.attrsOf types.optionSet; - options = [ directorOptions ]; + type = with types; attrsOf (submodule directorOptions); }; device = mkOption { @@ -262,8 +260,7 @@ in { description = '' This option defines Device resources in Bacula Storage Daemon. ''; - type = types.attrsOf types.optionSet; - options = [ deviceOptions ]; + type = with types; attrsOf (submodule deviceOptions); }; extraStorageConfig = mkOption { @@ -343,6 +340,7 @@ in { extraConfig = mkOption { default = ""; + type = types.lines; description = '' Extra configuration for Bacula Director Daemon. ''; diff --git a/nixos/modules/services/backup/crashplan.nix b/nixos/modules/services/backup/crashplan.nix index 38cf8eb72fb..d0af2e416b6 100644 --- a/nixos/modules/services/backup/crashplan.nix +++ b/nixos/modules/services/backup/crashplan.nix @@ -49,7 +49,7 @@ with lib; ensureDir ${crashplan.vardir}/backupArchives 700 ensureDir ${crashplan.vardir}/log 777 cp -avn ${crashplan}/conf.template/* ${crashplan.vardir}/conf - for x in app.asar bin EULA.txt install.vars lang lib libjniwrap64.so libjniwrap.so libjtux64.so libjtux.so libmd564.so libmd5.so share skin upgrade; do + for x in app.asar bin install.vars lang lib libc42archive64.so libc52archive.so libjniwrap64.so libjniwrap.so libjtux64.so libjtux.so libleveldb64.so libleveldb.so libmd564.so libmd5.so share skin upgrade; do rm -f ${crashplan.vardir}/$x; ln -sf ${crashplan}/$x ${crashplan.vardir}/$x; done diff --git a/nixos/modules/services/backup/rsnapshot.nix b/nixos/modules/services/backup/rsnapshot.nix index ce628a72036..16815bcc860 100644 --- a/nixos/modules/services/backup/rsnapshot.nix +++ b/nixos/modules/services/backup/rsnapshot.nix @@ -7,11 +7,14 @@ let cfgfile = pkgs.writeText "rsnapshot.conf" '' config_version 1.2 cmd_cp ${pkgs.coreutils}/bin/cp + cmd_rm ${pkgs.coreutils}/bin/rm cmd_rsync ${pkgs.rsync}/bin/rsync cmd_ssh ${pkgs.openssh}/bin/ssh cmd_logger ${pkgs.inetutils}/bin/logger cmd_du ${pkgs.coreutils}/bin/du + cmd_rsnapshot_diff ${pkgs.rsnapshot}/bin/rsnapshot-diff lockfile /run/rsnapshot.pid + link_dest 1 ${cfg.extraConfig} ''; diff --git a/nixos/modules/services/backup/tarsnap.nix b/nixos/modules/services/backup/tarsnap.nix index 24892a2a59a..67112343c33 100644 --- a/nixos/modules/services/backup/tarsnap.nix +++ b/nixos/modules/services/backup/tarsnap.nix @@ -1,25 +1,25 @@ -{ config, lib, pkgs, ... }: +{ config, lib, pkgs, utils, ... }: with lib; let - cfg = config.services.tarsnap; + gcfg = config.services.tarsnap; configFile = name: cfg: '' - cachedir ${config.services.tarsnap.cachedir}/${name} - keyfile ${cfg.keyfile} + keyfile ${cfg.keyfile} + ${optionalString (cfg.cachedir != null) "cachedir ${cfg.cachedir}"} ${optionalString cfg.nodump "nodump"} ${optionalString cfg.printStats "print-stats"} ${optionalString cfg.printStats "humanize-numbers"} ${optionalString (cfg.checkpointBytes != null) ("checkpoint-bytes "+cfg.checkpointBytes)} ${optionalString cfg.aggressiveNetworking "aggressive-networking"} - ${concatStringsSep "\n" (map (v: "exclude "+v) cfg.excludes)} - ${concatStringsSep "\n" (map (v: "include "+v) cfg.includes)} + ${concatStringsSep "\n" (map (v: "exclude ${v}") cfg.excludes)} + ${concatStringsSep "\n" (map (v: "include ${v}") cfg.includes)} ${optionalString cfg.lowmem "lowmem"} ${optionalString cfg.verylowmem "verylowmem"} - ${optionalString (cfg.maxbw != null) ("maxbw "+toString cfg.maxbw)} - ${optionalString (cfg.maxbwRateUp != null) ("maxbw-rate-up "+toString cfg.maxbwRateUp)} - ${optionalString (cfg.maxbwRateDown != null) ("maxbw-rate-down "+toString cfg.maxbwRateDown)} + ${optionalString (cfg.maxbw != null) "maxbw ${toString cfg.maxbw}"} + ${optionalString (cfg.maxbwRateUp != null) "maxbw-rate-up ${toString cfg.maxbwRateUp}"} + ${optionalString (cfg.maxbwRateDown != null) "maxbw-rate-down ${toString cfg.maxbwRateDown}"} ''; in { @@ -60,34 +60,13 @@ in ''; }; - cachedir = mkOption { - type = types.nullOr types.path; - default = "/var/cache/tarsnap"; - description = '' - The cache allows tarsnap to identify previously stored data - blocks, reducing archival time and bandwidth usage. - - Should the cache become desynchronized or corrupted, tarsnap - will refuse to run until you manually rebuild the cache with - <command>tarsnap --fsck</command>. - - Note that each individual archive (specified below) has its own cache - directory specified under <literal>cachedir</literal>; this is because - tarsnap locks the cache during backups, meaning multiple services - archives cannot be backed up concurrently or overlap with a shared - cache. - - Set to <literal>null</literal> to disable caching. - ''; - }; - archives = mkOption { - type = types.attrsOf (types.submodule ( + type = types.attrsOf (types.submodule ({ config, ... }: { options = { keyfile = mkOption { type = types.str; - default = config.services.tarsnap.keyfile; + default = gcfg.keyfile; description = '' Set a specific keyfile for this archive. This defaults to <literal>"/root/tarsnap.key"</literal> if left unspecified. @@ -107,6 +86,21 @@ in ''; }; + cachedir = mkOption { + type = types.nullOr types.path; + default = "/var/cache/tarsnap/${utils.escapeSystemdPath config.keyfile}"; + description = '' + The cache allows tarsnap to identify previously stored data + blocks, reducing archival time and bandwidth usage. + + Should the cache become desynchronized or corrupted, tarsnap + will refuse to run until you manually rebuild the cache with + <command>tarsnap --fsck</command>. + + Set to <literal>null</literal> to disable caching. + ''; + }; + nodump = mkOption { type = types.bool; default = true; @@ -249,7 +243,7 @@ in }; gamedata = - { directories = [ "/var/lib/minecraft "]; + { directories = [ "/var/lib/minecraft" ]; period = "*:30"; }; } @@ -262,8 +256,8 @@ in archive names are suffixed by a 1 second resolution timestamp. For each member of the set is created a timer which triggers the - instanced <literal>tarsnap@</literal> service unit. You may use - <command>systemctl start tarsnap@archive-name</command> to + instanced <literal>tarsnap-archive-name</literal> service unit. You may use + <command>systemctl start tarsnap-archive-name</command> to manually trigger creation of <literal>archive-name</literal> at any time. ''; @@ -271,63 +265,73 @@ in }; }; - config = mkIf cfg.enable { + config = mkIf gcfg.enable { assertions = (mapAttrsToList (name: cfg: { assertion = cfg.directories != []; message = "Must specify paths for tarsnap to back up"; - }) cfg.archives) ++ + }) gcfg.archives) ++ (mapAttrsToList (name: cfg: { assertion = !(cfg.lowmem && cfg.verylowmem); message = "You cannot set both lowmem and verylowmem"; - }) cfg.archives); - - systemd.services."tarsnap@" = { - description = "Tarsnap archive '%i'"; - requires = [ "network-online.target" ]; - after = [ "network-online.target" ]; - - path = [ pkgs.iputils pkgs.tarsnap pkgs.coreutils ]; - - # In order for the persistent tarsnap timer to work reliably, we have to - # make sure that the tarsnap server is reachable after systemd starts up - # the service - therefore we sleep in a loop until we can ping the - # endpoint. - preStart = "while ! ping -q -c 1 v1-0-0-server.tarsnap.com &> /dev/null; do sleep 3; done"; - scriptArgs = "%i"; - script = '' - mkdir -p -m 0755 ${dirOf cfg.cachedir} - mkdir -p -m 0700 ${cfg.cachedir} - chown root:root ${cfg.cachedir} - chmod 0700 ${cfg.cachedir} - mkdir -p -m 0700 ${cfg.cachedir}/$1 - DIRS=`cat /etc/tarsnap/$1.dirs` - exec tarsnap --configfile /etc/tarsnap/$1.conf -c -f $1-$(date +"%Y%m%d%H%M%S") $DIRS - ''; - - serviceConfig = { - IOSchedulingClass = "idle"; - NoNewPrivileges = "true"; - CapabilityBoundingSet = "CAP_DAC_READ_SEARCH"; - PermissionsStartOnly = "true"; - }; - }; + }) gcfg.archives); + + systemd.services = + mapAttrs' (name: cfg: nameValuePair "tarsnap-${name}" { + description = "Tarsnap archive '${name}'"; + requires = [ "network-online.target" ]; + after = [ "network-online.target" ]; + + path = [ pkgs.iputils pkgs.tarsnap pkgs.utillinux ]; + + # In order for the persistent tarsnap timer to work reliably, we have to + # make sure that the tarsnap server is reachable after systemd starts up + # the service - therefore we sleep in a loop until we can ping the + # endpoint. + preStart = '' + while ! ping -q -c 1 v1-0-0-server.tarsnap.com &> /dev/null; do sleep 3; done + ''; + + script = + let run = ''tarsnap --configfile "/etc/tarsnap/${name}.conf" -c -f "${name}-$(date +"%Y%m%d%H%M%S")" ${concatStringsSep " " cfg.directories}''; + in if (cfg.cachedir != null) then '' + mkdir -p ${cfg.cachedir} + chmod 0700 ${cfg.cachedir} + + ( flock 9 + if [ ! -e ${cfg.cachedir}/firstrun ]; then + ( flock 10 + flock -u 9 + tarsnap --configfile "/etc/tarsnap/${name}.conf" --fsck + flock 9 + ) 10>${cfg.cachedir}/firstrun + fi + ) 9>${cfg.cachedir}/lockf + + exec flock ${cfg.cachedir}/firstrun ${run} + '' else "exec ${run}"; + + serviceConfig = { + Type = "oneshot"; + IOSchedulingClass = "idle"; + NoNewPrivileges = "true"; + CapabilityBoundingSet = [ "CAP_DAC_READ_SEARCH" ]; + PermissionsStartOnly = "true"; + }; + }) gcfg.archives; # Note: the timer must be Persistent=true, so that systemd will start it even # if e.g. your laptop was asleep while the latest interval occurred. - systemd.timers = mapAttrs' (name: cfg: nameValuePair "tarsnap@${name}" + systemd.timers = mapAttrs' (name: cfg: nameValuePair "tarsnap-${name}" { timerConfig.OnCalendar = cfg.period; timerConfig.Persistent = "true"; wantedBy = [ "timers.target" ]; - }) cfg.archives; + }) gcfg.archives; environment.etc = - (mapAttrs' (name: cfg: nameValuePair "tarsnap/${name}.conf" + mapAttrs' (name: cfg: nameValuePair "tarsnap/${name}.conf" { text = configFile name cfg; - }) cfg.archives) // - (mapAttrs' (name: cfg: nameValuePair "tarsnap/${name}.dirs" - { text = concatStringsSep " " cfg.directories; - }) cfg.archives); + }) gcfg.archives; environment.systemPackages = [ pkgs.tarsnap ]; }; diff --git a/nixos/modules/services/cluster/fleet.nix b/nixos/modules/services/cluster/fleet.nix index 78d4ea93c49..ec03be39594 100644 --- a/nixos/modules/services/cluster/fleet.nix +++ b/nixos/modules/services/cluster/fleet.nix @@ -28,7 +28,7 @@ in { etcdServers = mkOption { type = types.listOf types.str; - default = [ "http://127.0.0.1:4001" ]; + default = [ "http://127.0.0.1:2379" ]; description = '' Fleet list of etcd endpoints to use. ''; diff --git a/nixos/modules/services/cluster/kubernetes.nix b/nixos/modules/services/cluster/kubernetes.nix index 42efde36678..fbf7412a6cd 100644 --- a/nixos/modules/services/cluster/kubernetes.nix +++ b/nixos/modules/services/cluster/kubernetes.nix @@ -5,14 +5,77 @@ with lib; let cfg = config.services.kubernetes; + skipAttrs = attrs: map (filterAttrs (k: v: k != "enable")) + (filter (v: !(hasAttr "enable" v) || v.enable) attrs); + + infraContainer = pkgs.dockerTools.buildImage { + name = "pause"; + tag = "latest"; + contents = cfg.package.pause; + config.Cmd = "/bin/pause"; + }; + + kubeconfig = pkgs.writeText "kubeconfig" (builtins.toJSON { + apiVersion = "v1"; + kind = "Config"; + clusters = [{ + name = "local"; + cluster.certificate-authority = cfg.kubeconfig.caFile; + cluster.server = cfg.kubeconfig.server; + }]; + users = [{ + name = "kubelet"; + user = { + client-certificate = cfg.kubeconfig.certFile; + client-key = cfg.kubeconfig.keyFile; + }; + }]; + contexts = [{ + context = { + cluster = "local"; + user = "kubelet"; + }; + current-context = "kubelet-context"; + }]; + }); + + policyFile = pkgs.writeText "kube-policy" + concatStringsSep "\n" (map (builtins.toJSON cfg.apiserver.authorizationPolicy)); + + cniConfig = pkgs.buildEnv { + name = "kubernetes-cni-config"; + paths = imap (i: entry: + pkgs.writeTextDir "${10+i}-${entry.type}.conf" (builtins.toJSON entry) + ) cfg.kubelet.cni.config; + }; + + manifests = pkgs.buildEnv { + name = "kubernetes-manifests"; + paths = mapAttrsToList (name: manifest: + pkgs.writeTextDir "${name}.json" (builtins.toJSON manifest) + ) cfg.kubelet.manifests; + }; + in { ###### interface options.services.kubernetes = { + roles = mkOption { + description = '' + Kubernetes role that this machine should take. + + Master role will enable etcd, apiserver, scheduler and controller manager + services. Node role will enable etcd, docker, kubelet and proxy services. + ''; + default = []; + type = types.listOf (types.enum ["master" "node"]); + }; + package = mkOption { description = "Kubernetes package to use."; type = types.package; + default = pkgs.kubernetes; }; verbose = mkOption { @@ -21,21 +84,56 @@ in { type = types.bool; }; - etcdServers = mkOption { - description = "Kubernetes list of etcd servers to watch."; - default = [ "127.0.0.1:4001" ]; - type = types.listOf types.str; + etcd = { + servers = mkOption { + description = "List of etcd servers. By default etcd is started, except if this option is changed."; + default = ["http://127.0.0.1:2379"]; + type = types.listOf types.str; + }; + + keyFile = mkOption { + description = "Etcd key file"; + default = null; + type = types.nullOr types.path; + }; + + certFile = mkOption { + description = "Etcd cert file"; + default = null; + type = types.nullOr types.path; + }; + + caFile = mkOption { + description = "Etcd ca file"; + default = null; + type = types.nullOr types.path; + }; }; - roles = mkOption { - description = '' - Kubernetes role that this machine should take. + kubeconfig = { + server = mkOption { + description = "Kubernetes apiserver server address"; + default = "http://${cfg.apiserver.address}:${toString cfg.apiserver.port}"; + type = types.str; + }; - Master role will enable etcd, apiserver, scheduler and controller manager - services. Node role will enable etcd, docker, kubelet and proxy services. - ''; - default = []; - type = types.listOf (types.enum ["master" "node"]); + caFile = mkOption { + description = "Certificate authrority file to use to connect to kuberentes apiserver"; + type = types.nullOr types.path; + default = null; + }; + + certFile = mkOption { + description = "Client certificate file to use to connect to kubernetes"; + type = types.nullOr types.path; + default = null; + }; + + keyFile = mkOption { + description = "Client key file to use to connect to kubernetes"; + type = types.nullOr types.path; + default = null; + }; }; dataDir = mkOption { @@ -44,12 +142,6 @@ in { type = types.path; }; - dockerCfg = mkOption { - description = "Kubernetes contents of dockercfg file."; - default = ""; - type = types.lines; - }; - apiserver = { enable = mkOption { description = "Whether to enable kubernetes apiserver."; @@ -72,6 +164,16 @@ in { type = types.str; }; + advertiseAddress = mkOption { + description = '' + Kubernetes apiserver IP address on which to advertise the apiserver + to members of the cluster. This address must be reachable by the rest + of the cluster. + ''; + default = null; + type = types.nullOr types.str; + }; + port = mkOption { description = "Kubernetes apiserver listening port."; default = 8080; @@ -80,41 +182,36 @@ in { securePort = mkOption { description = "Kubernetes apiserver secure port."; - default = 6443; + default = 443; type = types.int; }; tlsCertFile = mkOption { description = "Kubernetes apiserver certificate file."; - default = ""; - type = types.str; + default = null; + type = types.nullOr types.path; }; - tlsPrivateKeyFile = mkOption { + tlsKeyFile = mkOption { description = "Kubernetes apiserver private key file."; - default = ""; - type = types.str; + default = null; + type = types.nullOr types.path; }; clientCaFile = mkOption { description = "Kubernetes apiserver CA file for client auth."; - default = ""; - type = types.str; + default = null; + type = types.nullOr types.path; }; tokenAuth = mkOption { description = '' Kubernetes apiserver token authentication file. See - <link xlink:href="http://kubernetes.io/v1.0/docs/admin/authentication.html"/> + <link xlink:href="http://kubernetes.io/docs/admin/authentication.html"/> ''; - default = {}; - example = literalExample '' - { - alice = "abc123"; - bob = "xyz987"; - } - ''; - type = types.attrsOf types.str; + default = null; + example = ''token,user,uid,"group1,group2,group3"''; + type = types.nullOr types.lines; }; authorizationMode = mkOption { @@ -148,13 +245,13 @@ in { allowPrivileged = mkOption { description = "Whether to allow privileged containers on kubernetes."; - default = false; + default = true; type = types.bool; }; portalNet = mkOption { description = "Kubernetes CIDR notation IP range from which to assign portal IPs"; - default = "10.10.10.10/16"; + default = "10.10.10.10/24"; type = types.str; }; @@ -171,9 +268,9 @@ in { admissionControl = mkOption { description = '' Kubernetes admission control plugins to use. See - <link xlink:href="http://kubernetes.io/v1.0/docs/admin/admission-controllers.html"/> + <link xlink:href="http://kubernetes.io/docs/admin/admission-controllers/"/> ''; - default = ["AlwaysAdmit"]; + default = ["NamespaceLifecycle" "LimitRanger" "ServiceAccount" "ResourceQuota"]; example = [ "NamespaceLifecycle" "NamespaceExists" "LimitRanger" "SecurityContextDeny" "ServiceAccount" "ResourceQuota" @@ -181,15 +278,40 @@ in { type = types.listOf types.str; }; - serviceAccountKey = mkOption { + serviceAccountKeyFile = mkOption { description = '' Kubernetes apiserver PEM-encoded x509 RSA private or public key file, - used to verify ServiceAccount tokens. + used to verify ServiceAccount tokens. By default tls private key file + is used. ''; default = null; type = types.nullOr types.path; }; + kubeletClientCaFile = mkOption { + description = "Path to a cert file for connecting to kubelet"; + default = null; + type = types.nullOr types.path; + }; + + kubeletClientCertFile = mkOption { + description = "Client certificate to use for connections to kubelet"; + default = null; + type = types.nullOr types.path; + }; + + kubeletClientKeyFile = mkOption { + description = "Key to use for connections to kubelet"; + default = null; + type = types.nullOr types.path; + }; + + kubeletHttps = mkOption { + description = "Whether to use https for connections to kubelet"; + default = true; + type = types.bool; + }; + extraOpts = mkOption { description = "Kubernetes apiserver extra command line options."; default = ""; @@ -216,10 +338,10 @@ in { type = types.int; }; - master = mkOption { - description = "Kubernetes apiserver address"; - default = "${cfg.apiserver.address}:${toString cfg.apiserver.port}"; - type = types.str; + leaderElect = mkOption { + description = "Whether to start leader election before executing main loop"; + type = types.bool; + default = false; }; extraOpts = mkOption { @@ -248,13 +370,13 @@ in { type = types.int; }; - master = mkOption { - description = "Kubernetes apiserver address"; - default = "${cfg.apiserver.address}:${toString cfg.apiserver.port}"; - type = types.str; + leaderElect = mkOption { + description = "Whether to start leader election before executing main loop"; + type = types.bool; + default = false; }; - serviceAccountPrivateKey = mkOption { + serviceAccountKeyFile = mkOption { description = '' Kubernetes controller manager PEM-encoded private RSA key file used to sign service account tokens @@ -272,6 +394,12 @@ in { type = types.nullOr types.path; }; + clusterCidr = mkOption { + description = "Kubernetes controller manager CIDR Range for Pods in cluster"; + default = "10.10.0.0/16"; + type = types.str; + }; + extraOpts = mkOption { description = "Kubernetes controller manager extra command line options."; default = ""; @@ -292,6 +420,12 @@ in { type = types.bool; }; + registerSchedulable = mkOption { + description = "Register the node as schedulable. No-op if register-node is false."; + default = true; + type = types.bool; + }; + address = mkOption { description = "Kubernetes kubelet info server listening address."; default = "0.0.0.0"; @@ -304,6 +438,18 @@ in { type = types.int; }; + tlsCertFile = mkOption { + description = "File containing x509 Certificate for HTTPS."; + default = null; + type = types.nullOr types.path; + }; + + tlsKeyFile = mkOption { + description = "File containing x509 private key matching tlsCertFile."; + default = null; + type = types.nullOr types.path; + }; + healthz = { bind = mkOption { description = "Kubernetes kubelet healthz listening address."; @@ -326,19 +472,10 @@ in { allowPrivileged = mkOption { description = "Whether to allow kubernetes containers to request privileged mode."; - default = false; + default = true; type = types.bool; }; - apiServers = mkOption { - description = '' - Kubernetes kubelet list of Kubernetes API servers for publishing events, - and reading pods and services. - ''; - default = ["${cfg.apiserver.address}:${toString cfg.apiserver.port}"]; - type = types.listOf types.str; - }; - cadvisorPort = mkOption { description = "Kubernetes kubelet local cadvisor port."; default = 4194; @@ -347,16 +484,62 @@ in { clusterDns = mkOption { description = "Use alternative dns."; - default = ""; + default = "10.10.0.1"; type = types.str; }; clusterDomain = mkOption { description = "Use alternative domain."; - default = "kubernetes.io"; + default = "cluster.local"; type = types.str; }; + networkPlugin = mkOption { + description = "Network plugin to use by kubernetes"; + type = types.nullOr (types.enum ["cni" "kubenet"]); + default = "kubenet"; + }; + + cni = { + packages = mkOption { + description = "List of network plugin packages to install"; + type = types.listOf types.package; + default = []; + }; + + config = mkOption { + description = "Kubernetes CNI configuration"; + type = types.listOf types.attrs; + default = []; + example = literalExample '' + [{ + "cniVersion": "0.2.0", + "name": "mynet", + "type": "bridge", + "bridge": "cni0", + "isGateway": true, + "ipMasq": true, + "ipam": { + "type": "host-local", + "subnet": "10.22.0.0/16", + "routes": [ + { "dst": "0.0.0.0/0" } + ] + } + } { + "cniVersion": "0.2.0", + "type": "loopback" + }] + ''; + }; + }; + + manifests = mkOption { + description = "List of manifests to bootstrap with kubelet"; + type = types.attrsOf types.attrs; + default = {}; + }; + extraOpts = mkOption { description = "Kubernetes kubelet extra command line options."; default = ""; @@ -377,12 +560,6 @@ in { type = types.str; }; - master = mkOption { - description = "Kubernetes apiserver address"; - default = "${cfg.apiserver.address}:${toString cfg.apiserver.port}"; - type = types.str; - }; - extraOpts = mkOption { description = "Kubernetes proxy extra command line options."; default = ""; @@ -390,23 +567,23 @@ in { }; }; - kube2sky = { - enable = mkEnableOption "Whether to enable kube2sky dns service."; + dns = { + enable = mkEnableOption "kubernetes dns service."; - domain = mkOption { - description = "Kuberntes kube2sky domain under which all DNS names will be hosted."; - default = cfg.kubelet.clusterDomain; - type = types.str; + port = mkOption { + description = "Kubernetes dns listening port"; + default = 53; + type = types.int; }; - master = mkOption { - description = "Kubernetes apiserver address"; - default = "${cfg.apiserver.address}:${toString cfg.apiserver.port}"; + domain = mkOption { + description = "Kuberntes dns domain under which to create names."; + default = cfg.kubelet.clusterDomain; type = types.str; }; extraOpts = mkOption { - description = "Kubernetes kube2sky extra command line options."; + description = "Kubernetes dns extra command line options."; default = ""; type = types.str; }; @@ -416,50 +593,118 @@ in { ###### implementation config = mkMerge [ + (mkIf cfg.kubelet.enable { + systemd.services.kubelet = { + description = "Kubernetes Kubelet Service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" "docker.service" "kube-apiserver.service" ]; + path = with pkgs; [ gitMinimal openssh docker utillinux iproute ethtool thin-provisioning-tools iptables ]; + preStart = '' + docker load < ${infraContainer} + rm /opt/cni/bin/* || true + ${concatMapStringsSep "\n" (p: "ln -fs ${p.plugins}/* /opt/cni/bin") cfg.kubelet.cni.packages} + ''; + serviceConfig = { + ExecStart = ''${cfg.package}/bin/kubelet \ + --pod-manifest-path=${manifests} \ + --kubeconfig=${kubeconfig} \ + --require-kubeconfig \ + --address=${cfg.kubelet.address} \ + --port=${toString cfg.kubelet.port} \ + --register-node=${if cfg.kubelet.registerNode then "true" else "false"} \ + --register-schedulable=${if cfg.kubelet.registerSchedulable then "true" else "false"} \ + ${optionalString (cfg.kubelet.tlsCertFile != null) + "--tls-cert-file=${cfg.kubelet.tlsCertFile}"} \ + ${optionalString (cfg.kubelet.tlsKeyFile != null) + "--tls-private-key-file=${cfg.kubelet.tlsKeyFile}"} \ + --healthz-bind-address=${cfg.kubelet.healthz.bind} \ + --healthz-port=${toString cfg.kubelet.healthz.port} \ + --hostname-override=${cfg.kubelet.hostname} \ + --allow-privileged=${if cfg.kubelet.allowPrivileged then "true" else "false"} \ + --root-dir=${cfg.dataDir} \ + --cadvisor_port=${toString cfg.kubelet.cadvisorPort} \ + ${optionalString (cfg.kubelet.clusterDns != "") + "--cluster-dns=${cfg.kubelet.clusterDns}"} \ + ${optionalString (cfg.kubelet.clusterDomain != "") + "--cluster-domain=${cfg.kubelet.clusterDomain}"} \ + --pod-infra-container-image=pause \ + ${optionalString (cfg.kubelet.networkPlugin != null) + "--network-plugin=${cfg.kubelet.networkPlugin}"} \ + --cni-conf-dir=${cniConfig} \ + --reconcile-cidr \ + --hairpin-mode=hairpin-veth \ + ${optionalString cfg.verbose "--v=6 --log_flush_frequency=1s"} \ + ${cfg.kubelet.extraOpts} + ''; + WorkingDirectory = cfg.dataDir; + }; + }; + + environment.etc = mapAttrs' (name: manifest: + nameValuePair "kubernetes/manifests/${name}.json" { + text = builtins.toJSON manifest; + mode = "0755"; + } + ) cfg.kubelet.manifests; + + # Allways include cni plugins + services.kubernetes.kubelet.cni.packages = [pkgs.cni]; + }) + (mkIf cfg.apiserver.enable { systemd.services.kube-apiserver = { - description = "Kubernetes Api Server"; + description = "Kubernetes Kubelet Service"; wantedBy = [ "multi-user.target" ]; - requires = ["kubernetes-setup.service"]; - after = [ "network-interfaces.target" "etcd.service" ]; + after = [ "network.target" "docker.service" ]; serviceConfig = { - ExecStart = let - authorizationPolicyFile = - pkgs.writeText "kubernetes-policy" - (builtins.toJSON cfg.apiserver.authorizationPolicy); - tokenAuthFile = - pkgs.writeText "kubernetes-auth" - (concatImapStringsSep "\n" (i: v: v + "," + (toString i)) - (mapAttrsToList (name: token: token + "," + name) cfg.apiserver.tokenAuth)); - in ''${cfg.package}/bin/kube-apiserver \ - --etcd-servers=${concatMapStringsSep "," (f: "http://${f}") cfg.etcdServers} \ - --insecure-bind-address=${cfg.apiserver.address} \ + ExecStart = ''${cfg.package}/bin/kube-apiserver \ + --etcd-servers=${concatStringsSep "," cfg.etcd.servers} \ + ${optionalString (cfg.etcd.caFile != null) + "--etcd-cafile=${cfg.etcd.caFile}"} \ + ${optionalString (cfg.etcd.certFile != null) + "--etcd-certfile=${cfg.etcd.certFile}"} \ + ${optionalString (cfg.etcd.keyFile != null) + "--etcd-keyfile=${cfg.etcd.keyFile}"} \ --insecure-port=${toString cfg.apiserver.port} \ - --bind-address=${cfg.apiserver.publicAddress} \ + --bind-address=0.0.0.0 \ + ${optionalString (cfg.apiserver.advertiseAddress != null) + "--advertise-address=${cfg.apiserver.advertiseAddress}"} \ --allow-privileged=${if cfg.apiserver.allowPrivileged then "true" else "false"} \ - ${optionalString (cfg.apiserver.tlsCertFile!="") + ${optionalString (cfg.apiserver.tlsCertFile != null) "--tls-cert-file=${cfg.apiserver.tlsCertFile}"} \ - ${optionalString (cfg.apiserver.tlsPrivateKeyFile!="") - "--tls-private-key-file=${cfg.apiserver.tlsPrivateKeyFile}"} \ - ${optionalString (cfg.apiserver.tokenAuth!=[]) - "--token-auth-file=${tokenAuthFile}"} \ - ${optionalString (cfg.apiserver.clientCaFile!="") + ${optionalString (cfg.apiserver.tlsKeyFile != null) + "--tls-private-key-file=${cfg.apiserver.tlsKeyFile}"} \ + ${optionalString (cfg.apiserver.tokenAuth != null) + "--token-auth-file=${cfg.apiserver.tokenAuth}"} \ + --kubelet-https=${if cfg.apiserver.kubeletHttps then "true" else "false"} \ + ${optionalString (cfg.apiserver.kubeletClientCaFile != null) + "--kubelet-certificate-authority=${cfg.apiserver.kubeletClientCaFile}"} \ + ${optionalString (cfg.apiserver.kubeletClientCertFile != null) + "--kubelet-client-certificate=${cfg.apiserver.kubeletClientCertFile}"} \ + ${optionalString (cfg.apiserver.kubeletClientKeyFile != null) + "--kubelet-client-key=${cfg.apiserver.kubeletClientKeyFile}"} \ + ${optionalString (cfg.apiserver.clientCaFile != null) "--client-ca-file=${cfg.apiserver.clientCaFile}"} \ --authorization-mode=${cfg.apiserver.authorizationMode} \ ${optionalString (cfg.apiserver.authorizationMode == "ABAC") - "--authorization-policy-file=${authorizationPolicyFile}"} \ + "--authorization-policy-file=${policyFile}"} \ --secure-port=${toString cfg.apiserver.securePort} \ --service-cluster-ip-range=${cfg.apiserver.portalNet} \ - ${optionalString (cfg.apiserver.runtimeConfig!="") + ${optionalString (cfg.apiserver.runtimeConfig != "") "--runtime-config=${cfg.apiserver.runtimeConfig}"} \ --admission_control=${concatStringsSep "," cfg.apiserver.admissionControl} \ - ${optionalString (cfg.apiserver.serviceAccountKey!=null) - "--service-account-key-file=${cfg.apiserver.serviceAccountKey}"} \ - --logtostderr=true \ - ${optionalString cfg.verbose "--v=6 --log-flush-frequency=1s"} \ + ${optionalString (cfg.apiserver.serviceAccountKeyFile!=null) + "--service-account-key-file=${cfg.apiserver.serviceAccountKeyFile}"} \ + ${optionalString cfg.verbose "--v=6"} \ + ${optionalString cfg.verbose "--log-flush-frequency=1s"} \ ${cfg.apiserver.extraOpts} ''; + WorkingDirectory = cfg.dataDir; User = "kubernetes"; + Group = "kubernetes"; + AmbientCapabilities = "cap_net_bind_service"; + Restart = "on-failure"; + RestartSec = 5; }; }; }) @@ -468,17 +713,20 @@ in { systemd.services.kube-scheduler = { description = "Kubernetes Scheduler Service"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" "kubernetes-apiserver.service" ]; + after = [ "kube-apiserver.service" ]; serviceConfig = { ExecStart = ''${cfg.package}/bin/kube-scheduler \ --address=${cfg.scheduler.address} \ --port=${toString cfg.scheduler.port} \ - --master=${cfg.scheduler.master} \ - --logtostderr=true \ - ${optionalString cfg.verbose "--v=6 --log-flush-frequency=1s"} \ + --leader-elect=${if cfg.scheduler.leaderElect then "true" else "false"} \ + --kubeconfig=${kubeconfig} \ + ${optionalString cfg.verbose "--v=6"} \ + ${optionalString cfg.verbose "--log-flush-frequency=1s"} \ ${cfg.scheduler.extraOpts} ''; + WorkingDirectory = cfg.dataDir; User = "kubernetes"; + Group = "kubernetes"; }; }; }) @@ -487,113 +735,94 @@ in { systemd.services.kube-controller-manager = { description = "Kubernetes Controller Manager Service"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" "kubernetes-apiserver.service" ]; + after = [ "kube-apiserver.service" ]; serviceConfig = { ExecStart = ''${cfg.package}/bin/kube-controller-manager \ --address=${cfg.controllerManager.address} \ --port=${toString cfg.controllerManager.port} \ - --master=${cfg.controllerManager.master} \ - ${optionalString (cfg.controllerManager.serviceAccountPrivateKey!=null) - "--service-account-private-key-file=${cfg.controllerManager.serviceAccountPrivateKey}"} \ + --kubeconfig=${kubeconfig} \ + --leader-elect=${if cfg.controllerManager.leaderElect then "true" else "false"} \ + ${if (cfg.controllerManager.serviceAccountKeyFile!=null) + then "--service-account-private-key-file=${cfg.controllerManager.serviceAccountKeyFile}" + else "--service-account-private-key-file=/var/run/kubernetes/apiserver.key"} \ ${optionalString (cfg.controllerManager.rootCaFile!=null) "--root-ca-file=${cfg.controllerManager.rootCaFile}"} \ - --logtostderr=true \ - ${optionalString cfg.verbose "--v=6 --log-flush-frequency=1s"} \ + ${optionalString (cfg.controllerManager.clusterCidr!=null) + "--cluster-cidr=${cfg.controllerManager.clusterCidr}"} \ + --allocate-node-cidrs=true \ + ${optionalString cfg.verbose "--v=6"} \ + ${optionalString cfg.verbose "--log-flush-frequency=1s"} \ ${cfg.controllerManager.extraOpts} ''; + WorkingDirectory = cfg.dataDir; User = "kubernetes"; + Group = "kubernetes"; }; }; }) - (mkIf cfg.kubelet.enable { - systemd.services.kubelet = { - description = "Kubernetes Kubelet Service"; - wantedBy = [ "multi-user.target" ]; - requires = ["kubernetes-setup.service"]; - after = [ "network-interfaces.target" "etcd.service" "docker.service" ]; - path = [ pkgs.gitMinimal pkgs.openssh ]; - script = '' - export PATH="/bin:/sbin:/usr/bin:/usr/sbin:$PATH" - exec ${cfg.package}/bin/kubelet \ - --api-servers=${concatMapStringsSep "," (f: "http://${f}") cfg.kubelet.apiServers} \ - --register-node=${if cfg.kubelet.registerNode then "true" else "false"} \ - --address=${cfg.kubelet.address} \ - --port=${toString cfg.kubelet.port} \ - --healthz-bind-address=${cfg.kubelet.healthz.bind} \ - --healthz-port=${toString cfg.kubelet.healthz.port} \ - --hostname-override=${cfg.kubelet.hostname} \ - --allow-privileged=${if cfg.kubelet.allowPrivileged then "true" else "false"} \ - --root-dir=${cfg.dataDir} \ - --cadvisor_port=${toString cfg.kubelet.cadvisorPort} \ - ${optionalString (cfg.kubelet.clusterDns != "") - ''--cluster-dns=${cfg.kubelet.clusterDns}''} \ - ${optionalString (cfg.kubelet.clusterDomain != "") - ''--cluster-domain=${cfg.kubelet.clusterDomain}''} \ - --logtostderr=true \ - ${optionalString cfg.verbose "--v=6 --log_flush_frequency=1s"} \ - ${cfg.kubelet.extraOpts} - ''; - serviceConfig.WorkingDirectory = cfg.dataDir; - }; - }) - (mkIf cfg.proxy.enable { systemd.services.kube-proxy = { description = "Kubernetes Proxy Service"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" "etcd.service" ]; + after = [ "kube-apiserver.service" ]; + path = [pkgs.iptables]; serviceConfig = { ExecStart = ''${cfg.package}/bin/kube-proxy \ - --master=${cfg.proxy.master} \ + --kubeconfig=${kubeconfig} \ --bind-address=${cfg.proxy.address} \ - --logtostderr=true \ - ${optionalString cfg.verbose "--v=6 --log-flush-frequency=1s"} \ - ${cfg.proxy.extraOpts} + ${optionalString cfg.verbose "--v=6"} \ + ${optionalString cfg.verbose "--log-flush-frequency=1s"} \ + ${cfg.controllerManager.extraOpts} ''; - Restart = "always"; # Retry connection - RestartSec = "5s"; + WorkingDirectory = cfg.dataDir; }; }; }) - (mkIf cfg.kube2sky.enable { - systemd.services.kube2sky = { - description = "Kubernetes Dns Bridge Service"; + (mkIf cfg.dns.enable { + systemd.services.kube-dns = { + description = "Kubernetes Dns Service"; wantedBy = [ "multi-user.target" ]; - after = [ "network.target" "skydns.service" "etcd.service" "kubernetes-apiserver.service" ]; + after = [ "kube-apiserver.service" ]; serviceConfig = { - ExecStart = ''${cfg.package}/bin/kube2sky \ - -etcd-server=http://${head cfg.etcdServers} \ - -domain=${cfg.kube2sky.domain} \ - -kube_master_url=http://${cfg.kube2sky.master} \ - -logtostderr=true \ - ${optionalString cfg.verbose "--v=6 --log-flush-frequency=1s"} \ - ${cfg.kube2sky.extraOpts} + ExecStart = ''${cfg.package}/bin/kube-dns \ + --kubecfg-file=${kubeconfig} \ + --dns-port=${toString cfg.dns.port} \ + --domain=${cfg.dns.domain} \ + ${optionalString cfg.verbose "--v=6"} \ + ${optionalString cfg.verbose "--log-flush-frequency=1s"} \ + ${cfg.dns.extraOpts} ''; + WorkingDirectory = cfg.dataDir; User = "kubernetes"; + Group = "kubernetes"; + AmbientCapabilities = "cap_net_bind_service"; + SendSIGHUP = true; }; }; }) + (mkIf cfg.kubelet.enable { + boot.kernelModules = ["br_netfilter"]; + }) + (mkIf (any (el: el == "master") cfg.roles) { + virtualisation.docker.enable = mkDefault true; + services.kubernetes.kubelet.enable = mkDefault true; + services.kubernetes.kubelet.allowPrivileged = mkDefault true; services.kubernetes.apiserver.enable = mkDefault true; services.kubernetes.scheduler.enable = mkDefault true; services.kubernetes.controllerManager.enable = mkDefault true; - services.kubernetes.kube2sky.enable = mkDefault true; + services.etcd.enable = mkDefault (cfg.etcd.servers == ["http://127.0.0.1:2379"]); }) (mkIf (any (el: el == "node") cfg.roles) { virtualisation.docker.enable = mkDefault true; + virtualisation.docker.logDriver = mkDefault "json-file"; services.kubernetes.kubelet.enable = mkDefault true; services.kubernetes.proxy.enable = mkDefault true; - }) - - (mkIf (any (el: el == "node" || el == "master") cfg.roles) { - services.etcd.enable = mkDefault true; - - services.skydns.enable = mkDefault true; - services.skydns.domain = mkDefault cfg.kubelet.clusterDomain; + services.kubernetes.dns.enable = mkDefault true; }) (mkIf ( @@ -601,24 +830,16 @@ in { cfg.scheduler.enable || cfg.controllerManager.enable || cfg.kubelet.enable || - cfg.proxy.enable + cfg.proxy.enable || + cfg.dns.enable ) { - systemd.services.kubernetes-setup = { - description = "Kubernetes setup."; - serviceConfig.Type = "oneshot"; - script = '' - mkdir -p /var/run/kubernetes - chown kubernetes /var/lib/kubernetes - - rm ${cfg.dataDir}/.dockercfg || true - ln -fs ${pkgs.writeText "kubernetes-dockercfg" cfg.dockerCfg} ${cfg.dataDir}/.dockercfg - ''; - }; - - services.kubernetes.package = mkDefault pkgs.kubernetes; + systemd.tmpfiles.rules = [ + "d /opt/cni/bin 0755 root root -" + "d /var/run/kubernetes 0755 kubernetes kubernetes -" + "d /var/lib/kubernetes 0755 kubernetes kubernetes -" + ]; environment.systemPackages = [ cfg.package ]; - users.extraUsers = singleton { name = "kubernetes"; uid = config.ids.uids.kubernetes; @@ -630,6 +851,5 @@ in { }; users.extraGroups.kubernetes.gid = config.ids.gids.kubernetes; }) - ]; } diff --git a/nixos/modules/services/cluster/panamax.nix b/nixos/modules/services/cluster/panamax.nix index b47ff744fc2..4475e8d8c24 100644 --- a/nixos/modules/services/cluster/panamax.nix +++ b/nixos/modules/services/cluster/panamax.nix @@ -46,7 +46,7 @@ in { fleetctlEndpoint = mkOption { type = types.str; - default = "http://127.0.0.1:4001"; + default = "http://127.0.0.1:2379"; description = '' Panamax fleetctl endpoint. ''; diff --git a/nixos/modules/services/computing/boinc/client.nix b/nixos/modules/services/computing/boinc/client.nix new file mode 100644 index 00000000000..91bd463732d --- /dev/null +++ b/nixos/modules/services/computing/boinc/client.nix @@ -0,0 +1,88 @@ +{config, lib, pkgs, ...}: + +with lib; + +let + cfg = config.services.boinc; + allowRemoteGuiRpcFlag = optionalString cfg.allowRemoteGuiRpc "--allow_remote_gui_rpc"; + +in + { + options.services.boinc = { + enable = mkOption { + type = types.bool; + default = false; + example = true; + description = '' + Whether to enable the BOINC distributed computing client. If this + option is set to true, the boinc_client daemon will be run as a + background service. The boinccmd command can be used to control the + daemon. + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.boinc; + defaultText = "pkgs.boinc"; + description = '' + Which BOINC package to use. + ''; + }; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/boinc"; + description = '' + The directory in which to store BOINC's configuration and data files. + ''; + }; + + allowRemoteGuiRpc = mkOption { + type = types.bool; + default = false; + example = true; + description = '' + If set to true, any remote host can connect to and control this BOINC + client (subject to password authentication). If instead set to false, + only the hosts listed in <varname>dataDir</varname>/remote_hosts.cfg will be allowed to + connect. + + See also: <link xlink:href="http://boinc.berkeley.edu/wiki/Controlling_BOINC_remotely#Remote_access"/> + ''; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [cfg.package]; + + users.users.boinc = { + createHome = false; + description = "BOINC Client"; + home = cfg.dataDir; + isSystemUser = true; + }; + + systemd.services.boinc = { + description = "BOINC Client"; + after = ["network.target" "local-fs.target"]; + wantedBy = ["multi-user.target"]; + preStart = '' + mkdir -p ${cfg.dataDir} + chown boinc ${cfg.dataDir} + ''; + script = '' + ${cfg.package}/bin/boinc_client --dir ${cfg.dataDir} --redirectio ${allowRemoteGuiRpcFlag} + ''; + serviceConfig = { + PermissionsStartOnly = true; # preStart must be run as root + User = "boinc"; + Nice = 10; + }; + }; + }; + + meta = { + maintainers = with lib.maintainers; [kierdavis]; + }; + } diff --git a/nixos/modules/services/continuous-integration/buildbot/master.nix b/nixos/modules/services/continuous-integration/buildbot/master.nix new file mode 100644 index 00000000000..a40be4f546e --- /dev/null +++ b/nixos/modules/services/continuous-integration/buildbot/master.nix @@ -0,0 +1,250 @@ +# NixOS module for Buildbot continous integration server. + +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.buildbot-master; + escapeStr = s: escape ["'"] s; + masterCfg = pkgs.writeText "master.cfg" '' + from buildbot.plugins import * + factory = util.BuildFactory() + c = BuildmasterConfig = dict( + workers = [${concatStringsSep "," cfg.workers}], + protocols = { 'pb': {'port': ${cfg.bpPort} } }, + title = '${escapeStr cfg.title}', + titleURL = '${escapeStr cfg.titleUrl}', + buildbotURL = '${escapeStr cfg.buildbotUrl}', + db = dict(db_url='${escapeStr cfg.dbUrl}'), + www = dict(port=${toString cfg.port}), + change_source = [ ${concatStringsSep "," cfg.changeSource} ], + schedulers = [ ${concatStringsSep "," cfg.schedulers} ], + builders = [ ${concatStringsSep "," cfg.builders} ], + status = [ ${concatStringsSep "," cfg.status} ], + ) + for step in [ ${concatStringsSep "," cfg.factorySteps} ]: + factory.addStep(step) + + ${cfg.extraConfig} + ''; + + configFile = if cfg.masterCfg == null then masterCfg else cfg.masterCfg; + +in { + options = { + services.buildbot-master = { + + factorySteps = mkOption { + type = types.listOf types.str; + description = "Factory Steps"; + default = []; + example = [ + "steps.Git(repourl='git://github.com/buildbot/pyflakes.git', mode='incremental')" + "steps.ShellCommand(command=['trial', 'pyflakes'])" + ]; + }; + + changeSource = mkOption { + type = types.listOf types.str; + description = "List of Change Sources."; + default = []; + example = [ + "changes.GitPoller('git://github.com/buildbot/pyflakes.git', workdir='gitpoller-workdir', branch='master', pollinterval=300)" + ]; + }; + + enable = mkOption { + type = types.bool; + default = false; + description = "Whether to enable the Buildbot continuous integration server."; + }; + + extraConfig = mkOption { + type = types.str; + description = "Extra configuration to append to master.cfg"; + default = ""; + }; + + masterCfg = mkOption { + type = with types; nullOr path; + description = '' + Optionally pass path to raw master.cfg file. + Other options in this configuration will be ignored. + ''; + default = null; + example = literalExample '' + pkgs.writeText "master.cfg" "BuildmasterConfig = c = {}" + ''; + }; + + schedulers = mkOption { + type = types.listOf types.str; + description = "List of Schedulers."; + default = [ + "schedulers.SingleBranchScheduler(name='all', change_filter=util.ChangeFilter(branch='master'), treeStableTimer=None, builderNames=['runtests'])" + "schedulers.ForceScheduler(name='force',builderNames=['runtests'])" + ]; + }; + + builders = mkOption { + type = types.listOf types.str; + description = "List of Builders."; + default = [ + "util.BuilderConfig(name='runtests',workernames=['default-worker'],factory=factory)" + ]; + }; + + workers = mkOption { + type = types.listOf types.str; + description = "List of Workers."; + default = [ + "worker.Worker('default-worker', 'password')" + ]; + example = [ "worker.LocalWorker('default-worker')" ]; + }; + + status = mkOption { + default = []; + type = types.listOf types.str; + description = "List of status notification endpoints."; + }; + + user = mkOption { + default = "buildbot"; + type = types.str; + description = "User the buildbot server should execute under."; + }; + + group = mkOption { + default = "buildbot"; + type = types.str; + description = "Primary group of buildbot user."; + }; + + extraGroups = mkOption { + type = types.listOf types.str; + default = [ "nixbld" ]; + description = "List of extra groups that the buildbot user should be a part of."; + }; + + home = mkOption { + default = "/home/buildbot"; + type = types.path; + description = "Buildbot home directory."; + }; + + buildbotDir = mkOption { + default = "${cfg.home}/master"; + type = types.path; + description = "Specifies the Buildbot directory."; + }; + + bpPort = mkOption { + default = "9989"; + type = types.string; + example = "tcp:10000:interface=127.0.0.1"; + description = "Port where the master will listen to Buildbot Worker."; + }; + + listenAddress = mkOption { + default = "0.0.0.0"; + type = types.str; + description = "Specifies the bind address on which the buildbot HTTP interface listens."; + }; + + buildbotUrl = mkOption { + default = "http://localhost:8010/"; + type = types.str; + description = "Specifies the Buildbot URL."; + }; + + title = mkOption { + default = "Buildbot"; + type = types.str; + description = "Specifies the Buildbot Title."; + }; + + titleUrl = mkOption { + default = "Buildbot"; + type = types.str; + description = "Specifies the Buildbot TitleURL."; + }; + + dbUrl = mkOption { + default = "sqlite:///state.sqlite"; + type = types.str; + description = "Specifies the database connection string."; + }; + + port = mkOption { + default = 8010; + type = types.int; + description = "Specifies port number on which the buildbot HTTP interface listens."; + }; + + package = mkOption { + type = types.package; + default = pkgs.buildbot-ui; + description = '' + Package to use for buildbot. + <literal>buildbot-full</literal> is required in order to use local workers. + ''; + example = pkgs.buildbot-full; + }; + + packages = mkOption { + default = [ ]; + example = [ pkgs.git ]; + type = types.listOf types.package; + description = "Packages to add to PATH for the buildbot process."; + }; + }; + }; + + config = mkIf cfg.enable { + users.extraGroups = optional (cfg.group == "buildbot") { + name = "buildbot"; + }; + + users.extraUsers = optional (cfg.user == "buildbot") { + name = "buildbot"; + description = "buildbot user"; + isNormalUser = true; + createHome = true; + home = cfg.home; + group = cfg.group; + extraGroups = cfg.extraGroups; + useDefaultShell = true; + }; + + systemd.services.buildbot-master = { + description = "Buildbot Continuous Integration Server"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + path = cfg.packages; + + serviceConfig = { + Type = "forking"; + User = cfg.user; + Group = cfg.group; + WorkingDirectory = cfg.home; + ExecStart = "${cfg.package}/bin/buildbot start ${cfg.buildbotDir}"; + }; + + preStart = '' + mkdir -vp ${cfg.buildbotDir} + chown -c ${cfg.user}:${cfg.group} ${cfg.buildbotDir} + ln -sf ${configFile} ${cfg.buildbotDir}/master.cfg + ${cfg.package}/bin/buildbot create-master ${cfg.buildbotDir} + ''; + + postStart = '' + until [[ $(${pkgs.curl}/bin/curl -s --head -w '\n%{http_code}' http://localhost:${toString cfg.port} | tail -n1) =~ ^(200|403)$ ]]; do + sleep 1 + done + ''; + }; + }; + +} diff --git a/nixos/modules/services/continuous-integration/gitlab-runner.nix b/nixos/modules/services/continuous-integration/gitlab-runner.nix new file mode 100644 index 00000000000..1fe4d28f9f3 --- /dev/null +++ b/nixos/modules/services/continuous-integration/gitlab-runner.nix @@ -0,0 +1,51 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.gitlab-runner; + configFile = pkgs.writeText "config.toml" cfg.configText; +in +{ + options.services.gitlab-runner = { + enable = mkEnableOption "Gitlab Runner"; + + configText = mkOption { + description = "Verbatim config.toml to use"; + }; + + workDir = mkOption { + default = "/var/lib/gitlab-runner"; + type = types.path; + description = "The working directory used"; + }; + + }; + + config = mkIf cfg.enable { + systemd.services.gitlab-runner = { + description = "Gitlab Runner"; + after = [ "network.target" "docker.service" ]; + requires = [ "docker.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStart = ''${pkgs.gitlab-runner.bin}/bin/gitlab-runner run \ + --working-directory ${cfg.workDir} \ + --config ${configFile} \ + --service gitlab-runner \ + --user gitlab-runner \ + ''; + }; + }; + + users.extraUsers.gitlab-runner = { + group = "gitlab-runner"; + extraGroups = [ "docker" ]; + uid = config.ids.uids.gitlab-runner; + home = cfg.workDir; + createHome = true; + }; + + users.extraGroups.gitlab-runner.gid = config.ids.gids.gitlab-runner; + }; +} diff --git a/nixos/modules/services/continuous-integration/gocd-agent/default.nix b/nixos/modules/services/continuous-integration/gocd-agent/default.nix index 21f319f7fcf..05adb18fbe9 100644 --- a/nixos/modules/services/continuous-integration/gocd-agent/default.nix +++ b/nixos/modules/services/continuous-integration/gocd-agent/default.nix @@ -37,6 +37,7 @@ in { packages = mkOption { default = [ pkgs.stdenv pkgs.jre pkgs.git config.programs.ssh.package pkgs.nix ]; + defaultText = "[ pkgs.stdenv pkgs.jre pkgs.git config.programs.ssh.package pkgs.nix ]"; type = types.listOf types.package; description = '' Packages to add to PATH for the Go.CD agent process. @@ -98,7 +99,7 @@ in { ]; description = '' Specifies startup command line arguments to pass to Go.CD agent - java process. Example contains debug and gcLog arguments. + java process. ''; }; diff --git a/nixos/modules/services/continuous-integration/gocd-server/default.nix b/nixos/modules/services/continuous-integration/gocd-server/default.nix index 2d198630121..07e00f17f1e 100644 --- a/nixos/modules/services/continuous-integration/gocd-server/default.nix +++ b/nixos/modules/services/continuous-integration/gocd-server/default.nix @@ -68,6 +68,7 @@ in { packages = mkOption { default = [ pkgs.stdenv pkgs.jre pkgs.git config.programs.ssh.package pkgs.nix ]; + defaultText = "[ pkgs.stdenv pkgs.jre pkgs.git config.programs.ssh.package pkgs.nix ]"; type = types.listOf types.package; description = '' Packages to add to PATH for the Go.CD server's process. @@ -90,7 +91,7 @@ in { ''; }; - extraOptions = mkOption { + startupOptions = mkOption { default = [ "-Xms${cfg.initialJavaHeapSize}" "-Xmx${cfg.maxJavaHeapMemory}" @@ -103,6 +104,15 @@ in { "-Dcruise.server.port=${toString cfg.port}" "-Dcruise.server.ssl.port=${toString cfg.sslPort}" ]; + + description = '' + Specifies startup command line arguments to pass to Go.CD server + java process. + ''; + }; + + extraOptions = mkOption { + default = [ ]; example = [ "-X debug" "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005" @@ -169,7 +179,8 @@ in { script = '' ${pkgs.git}/bin/git config --global --add http.sslCAinfo /etc/ssl/certs/ca-certificates.crt - ${pkgs.jre}/bin/java -server ${concatStringsSep " " cfg.extraOptions} \ + ${pkgs.jre}/bin/java -server ${concatStringsSep " " cfg.startupOptions} \ + ${concatStringsSep " " cfg.extraOptions} \ -jar ${pkgs.gocd-server}/go-server/go.jar ''; diff --git a/nixos/modules/services/continuous-integration/hydra/default.nix b/nixos/modules/services/continuous-integration/hydra/default.nix index b1b3404add0..fa550f68b33 100644 --- a/nixos/modules/services/continuous-integration/hydra/default.nix +++ b/nixos/modules/services/continuous-integration/hydra/default.nix @@ -343,7 +343,7 @@ in { wantedBy = [ "multi-user.target" ]; requires = [ "hydra-init.service" ]; after = [ "hydra-init.service" "network.target" ]; - path = [ pkgs.nettools ]; + path = [ cfg.package pkgs.nettools ]; environment = env; serviceConfig = { ExecStart = "@${cfg.package}/bin/hydra-evaluator hydra-evaluator"; diff --git a/nixos/modules/services/databases/4store-endpoint.nix b/nixos/modules/services/databases/4store-endpoint.nix index 5c55ef406d5..906cb320df9 100644 --- a/nixos/modules/services/databases/4store-endpoint.nix +++ b/nixos/modules/services/databases/4store-endpoint.nix @@ -61,7 +61,9 @@ with lib; services.avahi.enable = true; systemd.services."4store-endpoint" = { - wantedBy = [ "ip-up.target" ]; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + script = '' ${run} '${pkgs.rdf4store}/bin/4s-httpd -D ${cfg.options} ${if cfg.listenAddress!=null then "-H ${cfg.listenAddress}" else "" } -p ${toString cfg.port} ${cfg.database}' ''; diff --git a/nixos/modules/services/databases/4store.nix b/nixos/modules/services/databases/4store.nix index 33e731e9681..62856822f90 100644 --- a/nixos/modules/services/databases/4store.nix +++ b/nixos/modules/services/databases/4store.nix @@ -53,7 +53,8 @@ with lib; services.avahi.enable = true; systemd.services."4store" = { - wantedBy = [ "ip-up.target" ]; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; preStart = '' mkdir -p ${stateDir}/ diff --git a/nixos/modules/services/databases/cassandra.nix b/nixos/modules/services/databases/cassandra.nix index c98af617587..b43b448ed7e 100644 --- a/nixos/modules/services/databases/cassandra.nix +++ b/nixos/modules/services/databases/cassandra.nix @@ -377,7 +377,7 @@ in { systemd.services.cassandra = { description = "Cassandra Daemon"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; environment = cassandraEnvironment; restartTriggers = [ cassandraConfFile cassandraLogFile cassandraRackFile ]; serviceConfig = { diff --git a/nixos/modules/services/databases/couchdb.nix b/nixos/modules/services/databases/couchdb.nix index ae0589b399e..d4d231456c5 100644 --- a/nixos/modules/services/databases/couchdb.nix +++ b/nixos/modules/services/databases/couchdb.nix @@ -162,7 +162,7 @@ in { if [ "$(id -u)" = 0 ]; then chown ${cfg.user}:${cfg.group} `dirname ${cfg.uriFile}`; - (-f ${cfg.uriFile} && chown ${cfg.user}:${cfg.group} ${cfg.uriFile}) || true + (test -f ${cfg.uriFile} && chown ${cfg.user}:${cfg.group} ${cfg.uriFile}) || true chown ${cfg.user}:${cfg.group} ${cfg.databaseDir} chown ${cfg.user}:${cfg.group} ${cfg.viewIndexDir} chown ${cfg.user}:${cfg.group} ${cfg.configFile} diff --git a/nixos/modules/services/databases/influxdb.nix b/nixos/modules/services/databases/influxdb.nix index e2268bd556e..dd88624f406 100644 --- a/nixos/modules/services/databases/influxdb.nix +++ b/nixos/modules/services/databases/influxdb.nix @@ -66,16 +66,16 @@ let enabled = false; }]; - collectd = { + collectd = [{ enabled = false; typesdb = "${pkgs.collectd}/share/collectd/types.db"; database = "collectd_db"; port = 25826; - }; + }]; - opentsdb = { + opentsdb = [{ enabled = false; - }; + }]; continuous_queries = { enabled = true; @@ -160,7 +160,7 @@ in systemd.services.influxdb = { description = "InfluxDB Server"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; serviceConfig = { ExecStart = ''${cfg.package}/bin/influxd -config "${configFile}"''; User = "${cfg.user}"; @@ -171,6 +171,11 @@ in mkdir -m 0770 -p ${cfg.dataDir} if [ "$(id -u)" = 0 ]; then chown -R ${cfg.user}:${cfg.group} ${cfg.dataDir}; fi ''; + postStart = mkBefore '' + until ${pkgs.curl.bin}/bin/curl -s -o /dev/null 'http://127.0.0.1${toString configOptions.http.bind-address}'/ping; do + sleep 1; + done + ''; }; users.extraUsers = optional (cfg.user == "influxdb") { diff --git a/nixos/modules/services/databases/monetdb.nix b/nixos/modules/services/databases/monetdb.nix deleted file mode 100644 index 9f09c71e005..00000000000 --- a/nixos/modules/services/databases/monetdb.nix +++ /dev/null @@ -1,88 +0,0 @@ -{ config, lib, pkgs, ... }: -let - cfg = config.services.monetdb; - monetdbUser = "monetdb"; -in -with lib; -{ - - ###### interface - - options = { - - services.monetdb = { - - enable = mkOption { - type = types.bool; - default = false; - description = "Whether to enable MonetDB database server."; - }; - - package = mkOption { - type = types.path; - description = "MonetDB package to use."; - }; - - dbfarmDir = mkOption { - type = types.path; - default = "/var/lib/monetdb"; - description = '' - Specifies location of Monetdb dbfarm (keeps database and auxiliary files). - ''; - }; - - port = mkOption { - default = "50000"; - example = "50000"; - description = "Port to listen on."; - }; - }; - - }; - - - ###### implementation - - config = mkIf cfg.enable { - - users.extraUsers.monetdb = - { name = monetdbUser; - uid = config.ids.uids.monetdb; - description = "monetdb user"; - home = cfg.dbfarmDir; - }; - - users.extraGroups.monetdb.gid = config.ids.gids.monetdb; - - environment.systemPackages = [ cfg.package ]; - - systemd.services.monetdb = - { description = "MonetDB Server"; - - wantedBy = [ "multi-user.target" ]; - - after = [ "network.target" ]; - - path = [ cfg.package ]; - - preStart = - '' - # Initialise the database. - if ! test -e ${cfg.dbfarmDir}/.merovingian_properties; then - mkdir -m 0700 -p ${cfg.dbfarmDir} - chown -R ${monetdbUser} ${cfg.dbfarmDir} - ${cfg.package}/bin/monetdbd create ${cfg.dbfarmDir} - ${cfg.package}/bin/monetdbd set port=${cfg.port} ${cfg.dbfarmDir} - fi - ''; - - serviceConfig.ExecStart = "${cfg.package}/bin/monetdbd start -n ${cfg.dbfarmDir}"; - - serviceConfig.ExecStop = "${cfg.package}/bin/monetdbd stop ${cfg.dbfarmDir}"; - - unitConfig.RequiresMountsFor = "${cfg.dbfarmDir}"; - }; - - }; - -} diff --git a/nixos/modules/services/databases/mongodb.nix b/nixos/modules/services/databases/mongodb.nix index ef9bc46e4a0..38e46a0c6ef 100644 --- a/nixos/modules/services/databases/mongodb.nix +++ b/nixos/modules/services/databases/mongodb.nix @@ -12,13 +12,11 @@ let mongoCnf = pkgs.writeText "mongodb.conf" '' - bind_ip = ${cfg.bind_ip} - ${optionalString cfg.quiet "quiet = true"} - dbpath = ${cfg.dbpath} - syslog = true - fork = true - pidfilepath = ${cfg.pidFile} - ${optionalString (cfg.replSetName != "") "replSet = ${cfg.replSetName}"} + net.bindIp: ${cfg.bind_ip} + ${optionalString cfg.quiet "systemLog.quiet: true"} + systemLog.destination: syslog + storage.dbPath: ${cfg.dbpath} + ${optionalString (cfg.replSetName != "") "replication.replSetName: ${cfg.replSetName}"} ${cfg.extraConfig} ''; @@ -84,9 +82,9 @@ in extraConfig = mkOption { default = ""; example = '' - nojournal = true + storage.journal.enabled: false ''; - description = "MongoDB extra configuration"; + description = "MongoDB extra configuration in YAML format"; }; }; @@ -112,7 +110,7 @@ in after = [ "network.target" ]; serviceConfig = { - ExecStart = "${mongodb}/bin/mongod --quiet --config ${mongoCnf}"; + ExecStart = "${mongodb}/bin/mongod --quiet --config ${mongoCnf} --fork --pidfilepath ${cfg.pidFile}"; User = cfg.user; PIDFile = cfg.pidFile; Type = "forking"; diff --git a/nixos/modules/services/databases/mysql.nix b/nixos/modules/services/databases/mysql.nix index 0b2f99f8fff..fcf1f123cfb 100644 --- a/nixos/modules/services/databases/mysql.nix +++ b/nixos/modules/services/databases/mysql.nix @@ -43,6 +43,7 @@ in services.mysql = { enable = mkOption { + type = types.bool; default = false; description = " Whether to enable the MySQL server. @@ -58,16 +59,19 @@ in }; port = mkOption { - default = "3306"; + type = types.int; + default = 3306; description = "Port of MySQL"; }; user = mkOption { + type = types.str; default = "mysql"; description = "User account under which MySQL runs"; }; dataDir = mkOption { + type = types.path; default = "/var/mysql"; # !!! should be /var/db/mysql description = "Location where MySQL stores its table files"; }; @@ -78,6 +82,7 @@ in }; extraOptions = mkOption { + type = types.lines; default = ""; example = '' key_buffer_size = 6G @@ -115,32 +120,39 @@ in replication = { role = mkOption { + type = types.enum [ "master" "slave" "none" ]; default = "none"; - description = "Role of the MySQL server instance. Can be either: master, slave or none"; + description = "Role of the MySQL server instance."; }; serverId = mkOption { + type = types.int; default = 1; description = "Id of the MySQL server instance. This number must be unique for each instance"; }; masterHost = mkOption { + type = types.str; description = "Hostname of the MySQL master server"; }; slaveHost = mkOption { + type = types.str; description = "Hostname of the MySQL slave server"; }; masterUser = mkOption { + type = types.str; description = "Username of the MySQL replication user"; }; masterPassword = mkOption { + type = types.str; description = "Password of the MySQL replication user"; }; masterPort = mkOption { + type = types.int; default = 3306; description = "Port number on which the MySQL master server runs"; }; @@ -167,6 +179,7 @@ in systemd.services.mysql = { description = "MySQL Server"; + after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; unitConfig.RequiresMountsFor = "${cfg.dataDir}"; diff --git a/nixos/modules/services/databases/neo4j.nix b/nixos/modules/services/databases/neo4j.nix index 41b96068590..7b51f1af689 100644 --- a/nixos/modules/services/databases/neo4j.nix +++ b/nixos/modules/services/databases/neo4j.nix @@ -5,34 +5,34 @@ with lib; let cfg = config.services.neo4j; - serverConfig = pkgs.writeText "neo4j-server.properties" '' - org.neo4j.server.database.location=${cfg.dataDir}/data/graph.db - org.neo4j.server.webserver.address=${cfg.listenAddress} - org.neo4j.server.webserver.port=${toString cfg.port} + serverConfig = pkgs.writeText "neo4j.conf" '' + dbms.directories.data=${cfg.dataDir}/data + dbms.directories.certificates=${cfg.certDir} + dbms.directories.logs=${cfg.dataDir}/logs + dbms.directories.plugins=${cfg.dataDir}/plugins + dbms.connector.http.type=HTTP + dbms.connector.http.enabled=true + dbms.connector.http.address=${cfg.listenAddress}:${toString cfg.port} + ${optionalString cfg.enableBolt '' + dbms.connector.bolt.type=BOLT + dbms.connector.bolt.enabled=true + dbms.connector.bolt.tls_level=OPTIONAL + dbms.connector.bolt.address=${cfg.listenAddress}:${toString cfg.boltPort} + ''} ${optionalString cfg.enableHttps '' - org.neo4j.server.webserver.https.enabled=true - org.neo4j.server.webserver.https.port=${toString cfg.httpsPort} - org.neo4j.server.webserver.https.cert.location=${cfg.cert} - org.neo4j.server.webserver.https.key.location=${cfg.key} - org.neo4j.server.webserver.https.keystore.location=${cfg.dataDir}/data/keystore + dbms.connector.https.type=HTTP + dbms.connector.https.enabled=true + dbms.connector.https.encryption=TLS + dbms.connector.https.address=${cfg.listenAddress}:${toString cfg.httpsPort} ''} - org.neo4j.server.webadmin.rrdb.location=${cfg.dataDir}/data/rrd - org.neo4j.server.webadmin.data.uri=/db/data/ - org.neo4j.server.webadmin.management.uri=/db/manage/ - org.neo4j.server.db.tuning.properties=${cfg.package}/share/neo4j/conf/neo4j.properties - org.neo4j.server.manage.console_engines=shell + dbms.shell.enabled=true ${cfg.extraServerConfig} ''; - loggingConfig = pkgs.writeText "logging.properties" cfg.loggingConfig; - wrapperConfig = pkgs.writeText "neo4j-wrapper.conf" '' - wrapper.java.additional=-Dorg.neo4j.server.properties=${serverConfig} - wrapper.java.additional=-Djava.util.logging.config.file=${loggingConfig} - wrapper.java.additional=-XX:+UseConcMarkSweepGC - wrapper.java.additional=-XX:+CMSClassUnloadingEnabled - wrapper.pidfile=${cfg.dataDir}/neo4j-server.pid - wrapper.name=neo4j + dbms.jvm.additional=-Dunsupported.dbms.udc.source=tarball + dbms.jvm.additional=-XX:+UseConcMarkSweepGC + dbms.jvm.additional=-XX:+CMSClassUnloadingEnabled ''; in { @@ -65,6 +65,18 @@ in { type = types.int; }; + enableBolt = mkOption { + description = "Enable bolt for Neo4j."; + default = true; + type = types.bool; + }; + + boltPort = mkOption { + description = "Neo4j port to listen for BOLT traffic."; + default = 7687; + type = types.int; + }; + enableHttps = mkOption { description = "Enable https for Neo4j."; default = false; @@ -77,15 +89,9 @@ in { type = types.int; }; - cert = mkOption { - description = "Neo4j https certificate."; - default = "${cfg.dataDir}/conf/ssl/neo4j.cert"; - type = types.path; - }; - - key = mkOption { - description = "Neo4j https certificate key."; - default = "${cfg.dataDir}/conf/ssl/neo4j.key"; + certDir = mkOption { + description = "Neo4j TLS certificates directory."; + default = "${cfg.dataDir}/certificates"; type = types.path; }; @@ -95,26 +101,11 @@ in { type = types.path; }; - loggingConfig = mkOption { - description = "Neo4j logging configuration."; - default = '' - handlers=java.util.logging.ConsoleHandler - .level=INFO - org.neo4j.server.level=INFO - - java.util.logging.ConsoleHandler.level=INFO - java.util.logging.ConsoleHandler.formatter=org.neo4j.server.logging.SimpleConsoleFormatter - java.util.logging.ConsoleHandler.filter=org.neo4j.server.logging.NeoLogFilter - ''; - type = types.lines; - }; - extraServerConfig = mkOption { description = "Extra configuration for neo4j server."; default = ""; type = types.lines; }; - }; ###### implementation @@ -123,15 +114,19 @@ in { systemd.services.neo4j = { description = "Neo4j Daemon"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" ]; - environment = { NEO4J_INSTANCE = cfg.dataDir; }; + after = [ "network.target" ]; + environment = { + NEO4J_HOME = "${cfg.package}/share/neo4j"; + NEO4J_CONF = "${cfg.dataDir}/conf"; + }; serviceConfig = { ExecStart = "${cfg.package}/bin/neo4j console"; User = "neo4j"; PermissionsStartOnly = true; }; preStart = '' - mkdir -m 0700 -p ${cfg.dataDir}/{data/graph.db,conf} + mkdir -m 0700 -p ${cfg.dataDir}/{data/graph.db,conf,logs} + ln -fs ${serverConfig} ${cfg.dataDir}/conf/neo4j.conf ln -fs ${wrapperConfig} ${cfg.dataDir}/conf/neo4j-wrapper.conf if [ "$(id -u)" = 0 ]; then chown -R neo4j ${cfg.dataDir}; fi ''; @@ -146,5 +141,4 @@ in { home = cfg.dataDir; }; }; - } diff --git a/nixos/modules/services/databases/openldap.nix b/nixos/modules/services/databases/openldap.nix index 9f22aa7c92b..b8e6c0cec3d 100644 --- a/nixos/modules/services/databases/openldap.nix +++ b/nixos/modules/services/databases/openldap.nix @@ -53,6 +53,13 @@ in description = "The database directory."; }; + configDir = mkOption { + type = types.nullOr types.path; + default = null; + description = "Use this optional config directory instead of using slapd.conf"; + example = "/var/db/slapd.d"; + }; + extraConfig = mkOption { type = types.lines; default = ""; @@ -96,7 +103,7 @@ in mkdir -p ${cfg.dataDir} chown -R ${cfg.user}:${cfg.group} ${cfg.dataDir} ''; - serviceConfig.ExecStart = "${openldap.out}/libexec/slapd -u ${cfg.user} -g ${cfg.group} -d 0 -h \"${concatStringsSep " " cfg.urlList}\" -f ${configFile}"; + serviceConfig.ExecStart = "${openldap.out}/libexec/slapd -u ${cfg.user} -g ${cfg.group} -d 0 -h \"${concatStringsSep " " cfg.urlList}\" ${if cfg.configDir == null then "-f "+configFile else "-F "+cfg.configDir}"; }; users.extraUsers.openldap = diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix index 9988fc6e63b..24ef4637ec9 100644 --- a/nixos/modules/services/databases/postgresql.nix +++ b/nixos/modules/services/databases/postgresql.nix @@ -11,12 +11,14 @@ let if cfg.extraPlugins == [] then pg else pkgs.buildEnv { name = "postgresql-and-plugins-${(builtins.parseDrvName pg.name).version}"; - paths = [ pg ] ++ cfg.extraPlugins; + paths = [ pg pg.lib ] ++ cfg.extraPlugins; + buildInputs = [ pkgs.makeWrapper ]; postBuild = '' mkdir -p $out/bin rm $out/bin/{pg_config,postgres,pg_ctl} cp --target-directory=$out/bin ${pg}/bin/{postgres,pg_config,pg_ctl} + wrapProgram $out/bin/postgres --set NIX_PGLIBDIR $out/lib ''; }; diff --git a/nixos/modules/services/databases/riak-cs.nix b/nixos/modules/services/databases/riak-cs.nix new file mode 100644 index 00000000000..198efc29222 --- /dev/null +++ b/nixos/modules/services/databases/riak-cs.nix @@ -0,0 +1,202 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.riak-cs; + +in + +{ + + ###### interface + + options = { + + services.riak-cs = { + + enable = mkEnableOption "riak-cs"; + + package = mkOption { + type = types.package; + default = pkgs.riak-cs; + defaultText = "pkgs.riak-cs"; + example = literalExample "pkgs.riak-cs"; + description = '' + Riak package to use. + ''; + }; + + nodeName = mkOption { + type = types.str; + default = "riak-cs@127.0.0.1"; + description = '' + Name of the Erlang node. + ''; + }; + + anonymousUserCreation = mkOption { + type = types.bool; + default = false; + description = '' + Anonymous user creation. + ''; + }; + + riakHost = mkOption { + type = types.str; + default = "127.0.0.1:8087"; + description = '' + Name of riak hosting service. + ''; + }; + + listener = mkOption { + type = types.str; + default = "127.0.0.1:8080"; + description = '' + Name of Riak CS listening service. + ''; + }; + + stanchionHost = mkOption { + type = types.str; + default = "127.0.0.1:8085"; + description = '' + Name of stanchion hosting service. + ''; + }; + + stanchionSsl = mkOption { + type = types.bool; + default = true; + description = '' + Tell stanchion to use SSL. + ''; + }; + + distributedCookie = mkOption { + type = types.str; + default = "riak"; + description = '' + Cookie for distributed node communication. All nodes in the + same cluster should use the same cookie or they will not be able to + communicate. + ''; + }; + + dataDir = mkOption { + type = types.path; + default = "/var/db/riak-cs"; + description = '' + Data directory for Riak CS. + ''; + }; + + logDir = mkOption { + type = types.path; + default = "/var/log/riak-cs"; + description = '' + Log directory for Riak CS. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Additional text to be appended to <filename>riak-cs.conf</filename>. + ''; + }; + + extraAdvancedConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Additional text to be appended to <filename>advanced.config</filename>. + ''; + }; + }; + + }; + + ###### implementation + + config = mkIf cfg.enable { + + environment.systemPackages = [ cfg.package ]; + environment.etc."riak-cs/riak-cs.conf".text = '' + nodename = ${cfg.nodeName} + distributed_cookie = ${cfg.distributedCookie} + + platform_log_dir = ${cfg.logDir} + + riak_host = ${cfg.riakHost} + listener = ${cfg.listener} + stanchion_host = ${cfg.stanchionHost} + + anonymous_user_creation = ${if cfg.anonymousUserCreation then "on" else "off"} + + ${cfg.extraConfig} + ''; + + environment.etc."riak-cs/advanced.config".text = '' + ${cfg.extraAdvancedConfig} + ''; + + users.extraUsers.riak-cs = { + name = "riak-cs"; + uid = config.ids.uids.riak-cs; + group = "riak"; + description = "Riak CS server user"; + }; + + systemd.services.riak-cs = { + description = "Riak CS Server"; + + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + path = [ + pkgs.utillinux # for `logger` + pkgs.bash + ]; + + environment.HOME = "${cfg.dataDir}"; + environment.RIAK_CS_DATA_DIR = "${cfg.dataDir}"; + environment.RIAK_CS_LOG_DIR = "${cfg.logDir}"; + environment.RIAK_CS_ETC_DIR = "/etc/riak"; + + preStart = '' + if ! test -e ${cfg.logDir}; then + mkdir -m 0755 -p ${cfg.logDir} + chown -R riak-cs ${cfg.logDir} + fi + + if ! test -e ${cfg.dataDir}; then + mkdir -m 0700 -p ${cfg.dataDir} + chown -R riak-cs ${cfg.dataDir} + fi + ''; + + serviceConfig = { + ExecStart = "${cfg.package}/bin/riak-cs console"; + ExecStop = "${cfg.package}/bin/riak-cs stop"; + StandardInput = "tty"; + User = "riak-cs"; + Group = "riak-cs"; + PermissionsStartOnly = true; + # Give Riak a decent amount of time to clean up. + TimeoutStopSec = 120; + LimitNOFILE = 65536; + }; + + unitConfig.RequiresMountsFor = [ + "${cfg.dataDir}" + "${cfg.logDir}" + "/etc/riak" + ]; + }; + }; +} diff --git a/nixos/modules/services/databases/riak.nix b/nixos/modules/services/databases/riak.nix index bee768fa42a..e0ebf164aef 100644 --- a/nixos/modules/services/databases/riak.nix +++ b/nixos/modules/services/databases/riak.nix @@ -20,7 +20,9 @@ in package = mkOption { type = types.package; - example = literalExample "pkgs.riak2"; + default = pkgs.riak; + defaultText = "pkgs.riak"; + example = literalExample "pkgs.riak"; description = '' Riak package to use. ''; @@ -68,6 +70,14 @@ in ''; }; + extraAdvancedConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Additional text to be appended to <filename>advanced.config</filename>. + ''; + }; + }; }; @@ -88,6 +98,10 @@ in ${cfg.extraConfig} ''; + environment.etc."riak/advanced.config".text = '' + ${cfg.extraAdvancedConfig} + ''; + users.extraUsers.riak = { name = "riak"; uid = config.ids.uids.riak; @@ -108,6 +122,7 @@ in pkgs.bash ]; + environment.HOME = "${cfg.dataDir}"; environment.RIAK_DATA_DIR = "${cfg.dataDir}"; environment.RIAK_LOG_DIR = "${cfg.logDir}"; environment.RIAK_ETC_DIR = "/etc/riak"; diff --git a/nixos/modules/services/databases/stanchion.nix b/nixos/modules/services/databases/stanchion.nix new file mode 100644 index 00000000000..f2dbb78b5c4 --- /dev/null +++ b/nixos/modules/services/databases/stanchion.nix @@ -0,0 +1,212 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.stanchion; + +in + +{ + + ###### interface + + options = { + + services.stanchion = { + + enable = mkEnableOption "stanchion"; + + package = mkOption { + type = types.package; + default = pkgs.stanchion; + defaultText = "pkgs.stanchion"; + example = literalExample "pkgs.stanchion"; + description = '' + Stanchion package to use. + ''; + }; + + nodeName = mkOption { + type = types.str; + default = "stanchion@127.0.0.1"; + description = '' + Name of the Erlang node. + ''; + }; + + adminKey = mkOption { + type = types.str; + default = ""; + description = '' + Name of admin user. + ''; + }; + + adminSecret = mkOption { + type = types.str; + default = ""; + description = '' + Name of admin secret + ''; + }; + + riakHost = mkOption { + type = types.str; + default = "127.0.0.1:8087"; + description = '' + Name of riak hosting service. + ''; + }; + + listener = mkOption { + type = types.str; + default = "127.0.0.1:8085"; + description = '' + Name of Riak CS listening service. + ''; + }; + + stanchionHost = mkOption { + type = types.str; + default = "127.0.0.1:8085"; + description = '' + Name of stanchion hosting service. + ''; + }; + + stanchionSsl = mkOption { + type = types.bool; + default = true; + description = '' + Tell stanchion to use SSL. + ''; + }; + + distributedCookie = mkOption { + type = types.str; + default = "riak"; + description = '' + Cookie for distributed node communication. All nodes in the + same cluster should use the same cookie or they will not be able to + communicate. + ''; + }; + + dataDir = mkOption { + type = types.path; + default = "/var/db/stanchion"; + description = '' + Data directory for Stanchion. + ''; + }; + + logDir = mkOption { + type = types.path; + default = "/var/log/stanchion"; + description = '' + Log directory for Stanchino. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Additional text to be appended to <filename>stanchion.conf</filename>. + ''; + }; + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + + environment.systemPackages = [ cfg.package ]; + + environment.etc."stanchion/advanced.config".text = '' + [{stanchion, []}]. + ''; + + environment.etc."stanchion/stanchion.conf".text = '' + listener = ${cfg.listener} + + riak_host = ${cfg.riakHost} + + ${optionalString (cfg.adminKey == "") "#"} admin.key=${optionalString (cfg.adminKey != "") cfg.adminKey} + ${optionalString (cfg.adminSecret == "") "#"} admin.secret=${optionalString (cfg.adminSecret != "") cfg.adminSecret} + + platform_bin_dir = ${pkgs.stanchion}/bin + platform_data_dir = ${cfg.dataDir} + platform_etc_dir = /etc/stanchion + platform_lib_dir = ${pkgs.stanchion}/lib + platform_log_dir = ${cfg.logDir} + + nodename = ${cfg.nodeName} + + distributed_cookie = ${cfg.distributedCookie} + + stanchion_ssl=${if cfg.stanchionSsl then "on" else "off"} + + ${cfg.extraConfig} + ''; + + users.extraUsers.stanchion = { + name = "stanchion"; + uid = config.ids.uids.stanchion; + group = "stanchion"; + description = "Stanchion server user"; + }; + + users.extraGroups.stanchion.gid = config.ids.gids.stanchion; + + systemd.services.stanchion = { + description = "Stanchion Server"; + + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + path = [ + pkgs.utillinux # for `logger` + pkgs.bash + ]; + + environment.HOME = "${cfg.dataDir}"; + environment.STANCHION_DATA_DIR = "${cfg.dataDir}"; + environment.STANCHION_LOG_DIR = "${cfg.logDir}"; + environment.STANCHION_ETC_DIR = "/etc/stanchion"; + + preStart = '' + if ! test -e ${cfg.logDir}; then + mkdir -m 0755 -p ${cfg.logDir} + chown -R stanchion:stanchion ${cfg.logDir} + fi + + if ! test -e ${cfg.dataDir}; then + mkdir -m 0700 -p ${cfg.dataDir} + chown -R stanchion:stanchion ${cfg.dataDir} + fi + ''; + + serviceConfig = { + ExecStart = "${cfg.package}/bin/stanchion console"; + ExecStop = "${cfg.package}/bin/stanchion stop"; + StandardInput = "tty"; + User = "stanchion"; + Group = "stanchion"; + PermissionsStartOnly = true; + # Give Stanchion a decent amount of time to clean up. + TimeoutStopSec = 120; + LimitNOFILE = 65536; + }; + + unitConfig.RequiresMountsFor = [ + "${cfg.dataDir}" + "${cfg.logDir}" + "/etc/stanchion" + ]; + }; + }; +} diff --git a/nixos/modules/services/databases/virtuoso.nix b/nixos/modules/services/databases/virtuoso.nix index bdd210a2550..3231fede08f 100644 --- a/nixos/modules/services/databases/virtuoso.nix +++ b/nixos/modules/services/databases/virtuoso.nix @@ -62,7 +62,8 @@ with lib; }; systemd.services.virtuoso = { - wantedBy = [ "ip-up.target" ]; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; preStart = '' mkdir -p ${stateDir} diff --git a/nixos/modules/services/desktops/accountsservice.nix b/nixos/modules/services/desktops/accountsservice.nix index c28c2729576..2a7450669ea 100644 --- a/nixos/modules/services/desktops/accountsservice.nix +++ b/nixos/modules/services/desktops/accountsservice.nix @@ -35,6 +35,14 @@ with lib; services.dbus.packages = [ pkgs.accountsservice ]; systemd.packages = [ pkgs.accountsservice ]; + + systemd.services.accounts-daemon= { + + wantedBy = [ "graphical.target" ]; + + } // (mkIf (!config.users.mutableUsers) { + environment.NIXOS_USERS_PURE = "true"; + }); }; } diff --git a/nixos/modules/services/desktops/gnome3/evolution-data-server.nix b/nixos/modules/services/desktops/gnome3/evolution-data-server.nix index a8f8da0eed5..2db2e2fe1c3 100644 --- a/nixos/modules/services/desktops/gnome3/evolution-data-server.nix +++ b/nixos/modules/services/desktops/gnome3/evolution-data-server.nix @@ -37,6 +37,8 @@ in services.dbus.packages = [ gnome3.evolution_data_server ]; + systemd.packages = [ gnome3.evolution_data_server ]; + }; } diff --git a/nixos/modules/services/desktops/gnome3/gnome-terminal-server.nix b/nixos/modules/services/desktops/gnome3/gnome-terminal-server.nix new file mode 100644 index 00000000000..384cede679c --- /dev/null +++ b/nixos/modules/services/desktops/gnome3/gnome-terminal-server.nix @@ -0,0 +1,44 @@ +# GNOME Documents daemon. + +{ config, pkgs, lib, ... }: + +with lib; + +let + gnome3 = config.environment.gnome3.packageSet; +in +{ + + ###### interface + + options = { + + services.gnome3.gnome-terminal-server = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable GNOME Terminal server service, + needed for gnome-terminal. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf config.services.gnome3.gnome-terminal-server.enable { + + environment.systemPackages = [ gnome3.gnome_terminal ]; + + services.dbus.packages = [ gnome3.gnome_terminal ]; + + systemd.packages = [ gnome3.gnome_terminal ]; + + }; + +} diff --git a/nixos/modules/services/desktops/gnome3/gvfs.nix b/nixos/modules/services/desktops/gnome3/gvfs.nix index a07cdadbb12..6bbabe8d3c5 100644 --- a/nixos/modules/services/desktops/gnome3/gvfs.nix +++ b/nixos/modules/services/desktops/gnome3/gvfs.nix @@ -37,6 +37,8 @@ in services.dbus.packages = [ gnome3.gvfs ]; + systemd.packages = [ gnome3.gvfs ]; + services.udev.packages = [ pkgs.libmtp.bin ]; }; diff --git a/nixos/modules/services/desktops/gnome3/tracker.nix b/nixos/modules/services/desktops/gnome3/tracker.nix index 8c5935a5ee3..dcaa60103a3 100644 --- a/nixos/modules/services/desktops/gnome3/tracker.nix +++ b/nixos/modules/services/desktops/gnome3/tracker.nix @@ -37,6 +37,8 @@ in services.dbus.packages = [ gnome3.tracker ]; + systemd.packages = [ gnome3.tracker ]; + }; } diff --git a/nixos/modules/services/desktops/profile-sync-daemon.nix b/nixos/modules/services/desktops/profile-sync-daemon.nix index d66ecef2385..e3f74df3e57 100644 --- a/nixos/modules/services/desktops/profile-sync-daemon.nix +++ b/nixos/modules/services/desktops/profile-sync-daemon.nix @@ -86,6 +86,12 @@ in { }; config = mkIf cfg.enable { + assertions = [ + { assertion = cfg.users != []; + message = "services.psd.users must contain at least one user"; + } + ]; + systemd = { services = { psd = { diff --git a/nixos/modules/services/editors/emacs.nix b/nixos/modules/services/editors/emacs.nix index 6795ec52fe4..08fa6de6374 100644 --- a/nixos/modules/services/editors/emacs.nix +++ b/nixos/modules/services/editors/emacs.nix @@ -79,9 +79,13 @@ in { environment.systemPackages = [ cfg.package editorScript ]; - environment.variables = if cfg.defaultEditor then { - EDITOR = mkOverride 900 "${editorScript}/bin/emacseditor"; - } else {}; + environment.variables = { + # This is required so that GTK applications launched from Emacs + # get properly themed: + GTK_DATA_PREFIX = "${config.system.path}"; + } // (if cfg.defaultEditor then { + EDITOR = mkOverride 900 "${editorScript}/bin/emacseditor"; + } else {}); }; meta.doc = ./emacs.xml; diff --git a/nixos/modules/services/editors/emacs.xml b/nixos/modules/services/editors/emacs.xml index ee8ef512bc7..e03f6046de8 100644 --- a/nixos/modules/services/editors/emacs.xml +++ b/nixos/modules/services/editors/emacs.xml @@ -43,9 +43,10 @@ <title>Installing <application>Emacs</application></title> <para> - Emacs can installed in the normal way for Nix (see <xref - linkend="sec-package-management" />). In addition, a NixOS - <emphasis>service</emphasis> can be enabled. + Emacs can be installed in the normal way for Nix (see + <xref linkend="sec-package-management" />). + In addition, a NixOS <emphasis>service</emphasis> + can be enabled. </para> <section> @@ -59,20 +60,20 @@ <variablelist> <varlistentry> <term><varname>emacs</varname></term> - <term><varname>emacs24</varname></term> + <term><varname>emacs25</varname></term> <listitem> <para> - The latest stable version of Emacs 24 using the <link + The latest stable version of Emacs 25 using the <link xlink:href="http://www.gtk.org">GTK+ 2</link> widget toolkit. </para> </listitem> </varlistentry> <varlistentry> - <term><varname>emacs24-nox</varname></term> + <term><varname>emacs25-nox</varname></term> <listitem> <para> - Emacs 24 built without any dependency on X11 + Emacs 25 built without any dependency on X11 libraries. </para> </listitem> @@ -86,15 +87,6 @@ </para> </listitem> </varlistentry> - <varlistentry> - <term><varname>emacs25pre</varname></term> - <listitem> - <para> - A pretest version of what will become the first - version of Emacs 25. - </para> - </listitem> - </varlistentry> </variablelist> </para> @@ -364,14 +356,14 @@ https://nixos.org/nixpkgs/manual/#sec-modify-via-packageOverrides <programlisting><![CDATA[ { pkgs ? import <nixpkgs> {} }: let - myEmacs = pkgs.lib.overrideDerivation (pkgs.emacs.override { + myEmacs = (pkgs.emacs.override { # Use gtk3 instead of the default gtk2 withGTK3 = true; withGTK2 = false; - }) (attrs: { + }).overrideAttrs (attrs: { # I don't want emacs.desktop file because I only use # emacsclient. - postInstall = attrs.postInstall + '' + postInstall = (attrs.postInstall or "") + '' rm $out/share/applications/emacs.desktop ''; }); @@ -573,6 +565,55 @@ services.emacs.install = true; <RET> nixos-rebuild <RET>.</literal> </para> </section> + + <section xml:id="sec-emacs-docbook-xml"> + <title>Editing DocBook 5 XML Documents</title> + <para> + Emacs includes <link + xlink:href="https://www.gnu.org/software/emacs/manual/html_node/nxml-mode/Introduction.html">nXML</link>, + a major-mode for validating and editing XML documents. + When editing DocBook 5.0 documents, such as + <link linkend="book-nixos-manual">this one</link>, + nXML needs to be configured with the relevant schema, which is + not included. + </para> + + <para> + To install the DocBook 5.0 schemas, either add + <varname>pkgs.docbook5</varname> to + <varname>environment.systemPackages</varname> (<link + linkend="sec-declarative-package-mgmt">NixOS</link>), or run + <literal>nix-env -i pkgs.docbook5</literal> + (<link linkend="sec-ad-hoc-packages">Nix</link>). + </para> + + <para> + Then customize the variable <varname>rng-schema-locating-files</varname> to include <filename>~/.emacs.d/schemas.xml</filename> and put the following text into that file: + <example xml:id="ex-emacs-docbook-xml"> + <title>nXML Schema Configuration (<filename>~/.emacs.d/schemas.xml</filename>)</title> + <programlisting language="xml"><![CDATA[ +<?xml version="1.0"?> +<!-- + To let emacs find this file, evaluate: + (add-to-list 'rng-schema-locating-files "~/.emacs.d/schemas.xml") +--> +<locatingRules xmlns="http://thaiopensource.com/ns/locating-rules/1.0"> + <!-- + Use this variation if pkgs.docbook5 is added to environment.systemPackages + --> + <namespace ns="http://docbook.org/ns/docbook" + uri="/run/current-system/sw/share/xml/docbook-5.0/rng/docbookxi.rnc"/> + <!-- + Use this variation if installing schema with "nix-env -iA pkgs.docbook5". + <namespace ns="http://docbook.org/ns/docbook" + uri="../.nix-profile/share/xml/docbook-5.0/rng/docbookxi.rnc"/> + --> +</locatingRules> +]]></programlisting> + </example> + </para> + + </section> </section> </chapter> diff --git a/nixos/modules/services/editors/infinoted.nix b/nixos/modules/services/editors/infinoted.nix new file mode 100644 index 00000000000..963147b18a0 --- /dev/null +++ b/nixos/modules/services/editors/infinoted.nix @@ -0,0 +1,158 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.infinoted; +in { + options.services.infinoted = { + enable = mkEnableOption "infinoted"; + + package = mkOption { + type = types.package; + default = pkgs.libinfinity.override { daemon = true; }; + defaultText = "pkgs.libinfinity.override { daemon = true; }"; + description = '' + Package providing infinoted + ''; + }; + + keyFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Private key to use for TLS + ''; + }; + + certificateFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Server certificate to use for TLS + ''; + }; + + certificateChain = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Chain of CA-certificates to which our `certificateFile` is relative. + Optional for TLS. + ''; + }; + + securityPolicy = mkOption { + type = types.enum ["no-tls" "allow-tls" "require-tls"]; + default = "require-tls"; + description = '' + How strictly to enforce clients connection with TLS. + ''; + }; + + port = mkOption { + type = types.int; + default = 6523; + description = '' + Port to listen on + ''; + }; + + rootDirectory = mkOption { + type = types.path; + default = "/var/lib/infinoted/documents/"; + description = '' + Root of the directory structure to serve + ''; + }; + + plugins = mkOption { + type = types.listOf types.str; + default = [ "note-text" "note-chat" "logging" "autosave" ]; + description = '' + Plugins to enable + ''; + }; + + passwordFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + File to read server-wide password from + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = '' + [autosave] + interval=10 + ''; + description = '' + Additional configuration to append to infinoted.conf + ''; + }; + + user = mkOption { + type = types.str; + default = "infinoted"; + description = '' + What to call the dedicated user under which infinoted is run + ''; + }; + + group = mkOption { + type = types.str; + default = "infinoted"; + description = '' + What to call the primary group of the dedicated user under which infinoted is run + ''; + }; + }; + + config = mkIf (cfg.enable) { + users.extraUsers = optional (cfg.user == "infinoted") + { name = "infinoted"; + description = "Infinoted user"; + group = cfg.group; + }; + users.extraGroups = optional (cfg.group == "infinoted") + { name = "infinoted"; + }; + + systemd.services.infinoted = + { description = "Gobby Dedicated Server"; + + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + Type = "simple"; + Restart = "always"; + ExecStart = "${cfg.package}/bin/infinoted-0.6 --config-file=/var/lib/infinoted/infinoted.conf"; + User = cfg.user; + Group = cfg.group; + PermissionsStartOnly = true; + }; + preStart = '' + mkdir -p /var/lib/infinoted + install -o ${cfg.user} -g ${cfg.group} -m 0600 /dev/null /var/lib/infinoted/infinoted.conf + cat >>/var/lib/infinoted/infinoted.conf <<EOF + [infinoted] + ${optionalString (cfg.keyFile != null) ''key-file=${cfg.keyFile}''} + ${optionalString (cfg.certificateFile != null) ''certificate-file=${cfg.certificateFile}''} + ${optionalString (cfg.certificateChain != null) ''certificate-chain=${cfg.certificateChain}''} + port=${toString cfg.port} + security-policy=${cfg.securityPolicy} + root-directory=${cfg.rootDirectory} + plugins=${concatStringsSep ";" cfg.plugins} + ${optionalString (cfg.passwordFile != null) ''password=$(head -n 1 ${cfg.passwordFile})''} + + ${cfg.extraConfig} + EOF + + install -o ${cfg.user} -g ${cfg.group} -m 0750 -d ${cfg.rootDirectory} + ''; + }; + }; +} diff --git a/nixos/modules/services/games/ghost-one.nix b/nixos/modules/services/games/ghost-one.nix index 5762148df2b..71ff6bb2f3f 100644 --- a/nixos/modules/services/games/ghost-one.nix +++ b/nixos/modules/services/games/ghost-one.nix @@ -21,8 +21,7 @@ in language = mkOption { default = "English"; - type = types.addCheck types.str - (lang: elem lang [ "English" "Spanish" "Russian" "Serbian" "Turkish" ]); + type = types.enum [ "English" "Spanish" "Russian" "Serbian" "Turkish" ]; description = "The language of bot messages: English, Spanish, Russian, Serbian or Turkish."; }; diff --git a/nixos/modules/services/games/terraria.nix b/nixos/modules/services/games/terraria.nix index 57ac37bb1bd..21aff780b67 100644 --- a/nixos/modules/services/games/terraria.nix +++ b/nixos/modules/services/games/terraria.nix @@ -64,7 +64,7 @@ in }; worldPath = mkOption { - type = types.path; + type = types.nullOr types.path; default = null; description = '' The path to the world file (<literal>.wld</literal>) which should be loaded. @@ -126,8 +126,8 @@ in User = "terraria"; Type = "oneshot"; RemainAfterExit = true; - ExecStart = "${pkgs.tmux.bin}/bin/tmux -S /var/lib/terraria/terraria.sock new -d ${pkgs.terraria-server}/bin/TerrariaServer ${concatStringsSep " " flags}"; - ExecStop = "${pkgs.tmux.bin}/bin/tmux -S /var/lib/terraria/terraria.sock send-keys Enter \"exit\" Enter"; + ExecStart = "${getBin pkgs.tmux}/bin/tmux -S /var/lib/terraria/terraria.sock new -d ${pkgs.terraria-server}/bin/TerrariaServer ${concatStringsSep " " flags}"; + ExecStop = "${getBin pkgs.tmux}/bin/tmux -S /var/lib/terraria/terraria.sock send-keys Enter \"exit\" Enter"; }; postStart = '' diff --git a/nixos/modules/services/hardware/brltty.nix b/nixos/modules/services/hardware/brltty.nix index 03e530b2c96..1266e8f81e5 100644 --- a/nixos/modules/services/hardware/brltty.nix +++ b/nixos/modules/services/hardware/brltty.nix @@ -28,7 +28,7 @@ in { }; serviceConfig = { ExecStart = "${pkgs.brltty}/bin/brltty --no-daemon"; - Type = "simple"; # Change to notidy after next releae + Type = "notify"; TimeoutStartSec = 5; TimeoutStopSec = 10; Restart = "always"; @@ -39,6 +39,8 @@ in { ProtectSystem = "full"; SystemCallArchitectures = "native"; }; + wants = [ "systemd-udev-settle.service" ]; + after = [ "local-fs.target" "systemd-udev-settle.service" ]; before = [ "sysinit.target" ]; wantedBy = [ "sysinit.target" ]; }; diff --git a/nixos/modules/services/hardware/pommed.nix b/nixos/modules/services/hardware/pommed.nix index 7be4dc1e846..bf7d6a46a29 100644 --- a/nixos/modules/services/hardware/pommed.nix +++ b/nixos/modules/services/hardware/pommed.nix @@ -2,7 +2,9 @@ with lib; -{ +let cfg = config.services.hardware.pommed; + defaultConf = "${pkgs.pommed_light}/etc/pommed.conf.mactel"; +in { options = { @@ -12,37 +14,37 @@ with lib; type = types.bool; default = false; description = '' - Whether to use the pommed tool to handle Apple laptop keyboard hotkeys. + Whether to use the pommed tool to handle Apple laptop + keyboard hotkeys. ''; }; configFile = mkOption { - type = types.path; + type = types.nullOr types.path; + default = null; description = '' - The path to the <filename>pommed.conf</filename> file. + The path to the <filename>pommed.conf</filename> file. Leave + to null to use the default config file + (<filename>/etc/pommed.conf.mactel</filename>). See the + files <filename>/etc/pommed.conf.mactel</filename> and + <filename>/etc/pommed.conf.pmac</filename> for examples to + build on. ''; }; }; }; - config = mkIf config.services.hardware.pommed.enable { - environment.systemPackages = [ pkgs.polkit ]; + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.polkit pkgs.pommed_light ]; - environment.etc."pommed.conf".source = config.services.hardware.pommed.configFile; - - services.hardware.pommed.configFile = "${pkgs.pommed}/etc/pommed.conf"; - - services.dbus.packages = [ pkgs.pommed ]; + environment.etc."pommed.conf".source = + if cfg.configFile == null then defaultConf else cfg.configFile; systemd.services.pommed = { - description = "Pommed hotkey management"; + description = "Pommed Apple Hotkeys Daemon"; wantedBy = [ "multi-user.target" ]; - after = [ "dbus.service" ]; - postStop = "rm -f /var/run/pommed.pid"; - script = "${pkgs.pommed}/bin/pommed"; - serviceConfig.Type = "forking"; - path = [ pkgs.eject ]; + script = "${pkgs.pommed_light}/bin/pommed -f"; }; }; } diff --git a/nixos/modules/services/hardware/sane.nix b/nixos/modules/services/hardware/sane.nix index a3403740312..8ddb9ef9c53 100644 --- a/nixos/modules/services/hardware/sane.nix +++ b/nixos/modules/services/hardware/sane.nix @@ -7,9 +7,35 @@ let pkg = if config.hardware.sane.snapshot then pkgs.sane-backends-git else pkgs.sane-backends; - backends = [ pkg ] ++ config.hardware.sane.extraBackends; + + sanedConf = pkgs.writeTextFile { + name = "saned.conf"; + destination = "/etc/sane.d/saned.conf"; + text = '' + localhost + ${config.services.saned.extraConfig} + ''; + }; + + netConf = pkgs.writeTextFile { + name = "net.conf"; + destination = "/etc/sane.d/net.conf"; + text = '' + ${lib.optionalString config.services.saned.enable "localhost"} + ${config.hardware.sane.netConf} + ''; + }; + + env = { + SANE_CONFIG_DIR = config.hardware.sane.configDir; + LD_LIBRARY_PATH = [ "${saneConfig}/lib/sane" ]; + }; + + backends = [ pkg netConf ] ++ optional config.services.saned.enable sanedConf ++ config.hardware.sane.extraBackends; saneConfig = pkgs.mkSaneConfig { paths = backends; }; + enabled = config.hardware.sane.enable || config.services.saned.enable; + in { @@ -51,27 +77,86 @@ in hardware.sane.configDir = mkOption { type = types.string; + internal = true; description = "The value of SANE_CONFIG_DIR."; }; - }; + hardware.sane.netConf = mkOption { + type = types.lines; + default = ""; + example = "192.168.0.16"; + description = '' + Network hosts that should be probed for remote scanners. + ''; + }; + services.saned.enable = mkOption { + type = types.bool; + default = false; + description = '' + Enable saned network daemon for remote connection to scanners. - ###### implementation + saned would be runned from <literal>scanner</literal> user; to allow + access to hardware that doesn't have <literal>scanner</literal> group + you should add needed groups to this user. + ''; + }; - config = mkIf config.hardware.sane.enable { + services.saned.extraConfig = mkOption { + type = types.lines; + default = ""; + example = "192.168.0.0/24"; + description = '' + Extra saned configuration lines. + ''; + }; - hardware.sane.configDir = mkDefault "${saneConfig}/etc/sane.d"; + }; - environment.systemPackages = backends; - environment.sessionVariables = { - SANE_CONFIG_DIR = config.hardware.sane.configDir; - LD_LIBRARY_PATH = [ "${saneConfig}/lib/sane" ]; - }; - services.udev.packages = backends; - users.extraGroups."scanner".gid = config.ids.gids.scanner; + ###### implementation - }; + config = mkMerge [ + (mkIf enabled { + hardware.sane.configDir = mkDefault "${saneConfig}/etc/sane.d"; + + environment.systemPackages = backends; + environment.sessionVariables = env; + services.udev.packages = backends; + + users.extraGroups."scanner".gid = config.ids.gids.scanner; + }) + + (mkIf config.services.saned.enable { + networking.firewall.connectionTrackingModules = [ "sane" ]; + + systemd.services."saned@" = { + description = "Scanner Service"; + environment = mapAttrs (name: val: toString val) env; + serviceConfig = { + User = "scanner"; + Group = "scanner"; + ExecStart = "${pkg}/bin/saned"; + }; + }; + + systemd.sockets.saned = { + description = "saned incoming socket"; + wantedBy = [ "sockets.target" ]; + listenStreams = [ "0.0.0.0:6566" "[::]:6566" ]; + socketConfig = { + # saned needs to distinguish between IPv4 and IPv6 to open matching data sockets. + BindIPv6Only = "ipv6-only"; + Accept = true; + MaxConnections = 1; + }; + }; + + users.extraUsers."scanner" = { + uid = config.ids.uids.scanner; + group = "scanner"; + }; + }) + ]; } diff --git a/nixos/modules/services/hardware/sane_extra_backends/brscan4.nix b/nixos/modules/services/hardware/sane_extra_backends/brscan4.nix index 3ec74458cd2..1923addeb3a 100644 --- a/nixos/modules/services/hardware/sane_extra_backends/brscan4.nix +++ b/nixos/modules/services/hardware/sane_extra_backends/brscan4.nix @@ -81,12 +81,11 @@ in { office1 = { model = "MFC-7860DW"; ip = "192.168.1.2"; }; office2 = { model = "MFC-7860DW"; nodename = "BRW0080927AFBCE"; }; }; - type = types.loaOf types.optionSet; + type = with types; loaOf (submodule netDeviceOpts); description = '' The list of network devices that will be registered against the brscan4 sane backend. ''; - options = [ netDeviceOpts ]; }; }; @@ -113,4 +112,4 @@ in ]; }; -} \ No newline at end of file +} diff --git a/nixos/modules/services/hardware/tlp.nix b/nixos/modules/services/hardware/tlp.nix index 281d02a8c65..f36a9e7b459 100644 --- a/nixos/modules/services/hardware/tlp.nix +++ b/nixos/modules/services/hardware/tlp.nix @@ -40,7 +40,7 @@ in }; extraConfig = mkOption { - type = types.str; + type = types.lines; default = ""; description = "Additional configuration variables for TLP"; }; diff --git a/nixos/modules/services/hardware/udev.nix b/nixos/modules/services/hardware/udev.nix index 7c4c93d0fcb..14d65978c32 100644 --- a/nixos/modules/services/hardware/udev.nix +++ b/nixos/modules/services/hardware/udev.nix @@ -32,13 +32,11 @@ let ''; # Perform substitutions in all udev rules files. - udevRules = stdenv.mkDerivation { - name = "udev-rules"; - - preferLocalBuild = true; - allowSubstitutes = false; - - buildCommand = '' + udevRules = pkgs.runCommand "udev-rules" + { preferLocalBuild = true; + allowSubstitutes = false; + } + '' mkdir -p $out shopt -s nullglob set +o pipefail @@ -130,15 +128,12 @@ let ln -s /dev/null $out/80-drivers.rules ''} ''; # */ - }; - hwdbBin = stdenv.mkDerivation { - name = "hwdb.bin"; - - preferLocalBuild = true; - allowSubstitutes = false; - - buildCommand = '' + hwdbBin = pkgs.runCommand "hwdb.bin" + { preferLocalBuild = true; + allowSubstitutes = false; + } + '' mkdir -p etc/udev/hwdb.d for i in ${toString ([udev] ++ cfg.packages)}; do echo "Adding hwdb files for package $i" @@ -151,7 +146,6 @@ let ${udev}/bin/udevadm hwdb --update --root=$(pwd) mv etc/udev/hwdb.bin $out ''; - }; # Udev has a 512-character limit for ENV{PATH}, so create a symlink # tree to work around this. diff --git a/nixos/modules/services/logging/logcheck.nix b/nixos/modules/services/logging/logcheck.nix index 755599ff621..86451ec318c 100644 --- a/nixos/modules/services/logging/logcheck.nix +++ b/nixos/modules/services/logging/logcheck.nix @@ -55,49 +55,53 @@ let levelOption = mkOption { default = "server"; - type = types.str; + type = types.enum [ "workstation" "server" "paranoid" ]; description = '' - Set the logcheck level. Either "workstation", "server", or "paranoid". + Set the logcheck level. ''; }; ignoreOptions = { - level = levelOption; + options = { + level = levelOption; - regex = mkOption { - default = ""; - type = types.str; - description = '' - Regex specifying which log lines to ignore. - ''; + regex = mkOption { + default = ""; + type = types.str; + description = '' + Regex specifying which log lines to ignore. + ''; + }; }; }; ignoreCronOptions = { - user = mkOption { - default = "root"; - type = types.str; - description = '' - User that runs the cronjob. - ''; - }; + options = { + user = mkOption { + default = "root"; + type = types.str; + description = '' + User that runs the cronjob. + ''; + }; - cmdline = mkOption { - default = ""; - type = types.str; - description = '' - Command line for the cron job. Will be turned into a regex for the logcheck ignore rule. - ''; - }; + cmdline = mkOption { + default = ""; + type = types.str; + description = '' + Command line for the cron job. Will be turned into a regex for the logcheck ignore rule. + ''; + }; - timeArgs = mkOption { - default = null; - type = types.nullOr (types.str); - example = "02 06 * * *"; - description = '' - "min hr dom mon dow" crontab time args, to auto-create a cronjob too. - Leave at null to not do this and just add a logcheck ignore rule. - ''; + timeArgs = mkOption { + default = null; + type = types.nullOr (types.str); + example = "02 06 * * *"; + description = '' + "min hr dom mon dow" crontab time args, to auto-create a cronjob too. + Leave at null to not do this and just add a logcheck ignore rule. + ''; + }; }; }; @@ -180,8 +184,7 @@ in description = '' This option defines extra ignore rules. ''; - type = types.loaOf types.optionSet; - options = [ ignoreOptions ]; + type = with types; loaOf (submodule ignoreOptions); }; ignoreCron = mkOption { @@ -189,8 +192,7 @@ in description = '' This option defines extra ignore rules for cronjobs. ''; - type = types.loaOf types.optionSet; - options = [ ignoreOptions ignoreCronOptions ]; + type = with types; loaOf (submodule ignoreCronOptions); }; extraGroups = mkOption { diff --git a/nixos/modules/services/logging/syslogd.nix b/nixos/modules/services/logging/syslogd.nix index a0f8e89fa69..fe0b0490811 100644 --- a/nixos/modules/services/logging/syslogd.nix +++ b/nixos/modules/services/logging/syslogd.nix @@ -100,6 +100,12 @@ in config = mkIf cfg.enable { + assertions = + [ { assertion = !config.services.rsyslogd.enable; + message = "rsyslogd conflicts with syslogd"; + } + ]; + environment.systemPackages = [ pkgs.sysklogd ]; services.syslogd.extraParams = optional cfg.enableNetworkInput "-r"; diff --git a/nixos/modules/services/mail/dovecot.nix b/nixos/modules/services/mail/dovecot.nix index 7848288850a..1d427429b9c 100644 --- a/nixos/modules/services/mail/dovecot.nix +++ b/nixos/modules/services/mail/dovecot.nix @@ -111,7 +111,7 @@ in }; extraConfig = mkOption { - type = types.str; + type = types.lines; default = ""; example = "mail_debug = yes"; description = "Additional entries to put verbatim into Dovecot's config file."; @@ -271,6 +271,9 @@ in { assertion = cfg.showPAMFailure -> cfg.enablePAM; message = "dovecot is configured with showPAMFailure while enablePAM is disabled"; } + { assertion = (cfg.sieveScripts != {}) -> ((cfg.mailUser != null) && (cfg.mailGroup != null)); + message = "dovecot requires mailUser and mailGroup to be set when sieveScripts is set"; + } ]; }; diff --git a/nixos/modules/services/mail/freepops.nix b/nixos/modules/services/mail/freepops.nix index e8c30a36923..5b729ca50a5 100644 --- a/nixos/modules/services/mail/freepops.nix +++ b/nixos/modules/services/mail/freepops.nix @@ -74,7 +74,8 @@ in config = mkIf cfg.enable { systemd.services.freepopsd = { description = "Freepopsd (webmail over POP3)"; - wantedBy = [ "ip-up.target" ]; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; script = '' ${pkgs.freepops}/bin/freepopsd \ -p ${toString cfg.port} \ diff --git a/nixos/modules/services/networking/offlineimap.nix b/nixos/modules/services/mail/offlineimap.nix index daf6196d370..85ece020905 100644 --- a/nixos/modules/services/networking/offlineimap.nix +++ b/nixos/modules/services/mail/offlineimap.nix @@ -59,7 +59,7 @@ in { }; path = cfg.path; }; - environment.systemPackages = [ "${cfg.package}" ]; + environment.systemPackages = [ cfg.package ]; systemd.user.timers.offlineimap = { description = "offlineimap timer"; timerConfig = { diff --git a/nixos/modules/services/mail/opensmtpd.nix b/nixos/modules/services/mail/opensmtpd.nix index fb94560e10a..53acdba4245 100644 --- a/nixos/modules/services/mail/opensmtpd.nix +++ b/nixos/modules/services/mail/opensmtpd.nix @@ -1,17 +1,16 @@ { config, lib, pkgs, ... }: -with pkgs; with lib; let cfg = config.services.opensmtpd; - conf = writeText "smtpd.conf" cfg.serverConfiguration; + conf = pkgs.writeText "smtpd.conf" cfg.serverConfiguration; args = concatStringsSep " " cfg.extraServerArgs; sendmail = pkgs.runCommand "opensmtpd-sendmail" {} '' mkdir -p $out/bin - ln -s ${opensmtpd}/sbin/smtpctl $out/bin/sendmail + ln -s ${pkgs.opensmtpd}/sbin/smtpctl $out/bin/sendmail ''; in { @@ -48,21 +47,19 @@ in { }; serverConfiguration = mkOption { - type = types.string; - default = ""; + type = types.lines; example = '' listen on lo accept for any deliver to lmtp localhost:24 - ''; + ''; description = '' The contents of the smtpd.conf configuration file. See the - OpenSMTPD documentation for syntax information. If this option - is left empty, the OpenSMTPD server will not start. + OpenSMTPD documentation for syntax information. ''; }; procPackages = mkOption { - type = types.listOf types.path; + type = types.listOf types.package; default = []; description = '' Packages to search for filters, tables, queues, and schedulers. @@ -100,12 +97,11 @@ in { systemd.services.opensmtpd = let procEnv = pkgs.buildEnv { name = "opensmtpd-procs"; - paths = [ opensmtpd ] ++ cfg.procPackages; + paths = [ pkgs.opensmtpd ] ++ cfg.procPackages; pathsToLink = [ "/libexec/opensmtpd" ]; }; in { wantedBy = [ "multi-user.target" ]; - wants = [ "network.target" ]; after = [ "network.target" ]; preStart = '' mkdir -p /var/spool/smtpd @@ -119,7 +115,7 @@ in { chown smtpq.root /var/spool/smtpd/purge chmod 700 /var/spool/smtpd/purge ''; - serviceConfig.ExecStart = "${opensmtpd}/sbin/smtpd -d -f ${conf} ${args}"; + serviceConfig.ExecStart = "${pkgs.opensmtpd}/sbin/smtpd -d -f ${conf} ${args}"; environment.OPENSMTPD_PROC_PATH = "${procEnv}/libexec/opensmtpd"; }; diff --git a/nixos/modules/services/mail/postgrey.nix b/nixos/modules/services/mail/postgrey.nix new file mode 100644 index 00000000000..d4ae25c066a --- /dev/null +++ b/nixos/modules/services/mail/postgrey.nix @@ -0,0 +1,194 @@ +{ config, lib, pkgs, ... }: + +with lib; let + + cfg = config.services.postgrey; + + natural = with types; addCheck int (x: x >= 0); + natural' = with types; addCheck int (x: x > 0); + + socket = with types; addCheck (either (submodule unixSocket) (submodule inetSocket)) (x: x ? "path" || x ? "port"); + + inetSocket = with types; { + options = { + addr = mkOption { + type = nullOr string; + default = null; + example = "127.0.0.1"; + description = "The address to bind to. Localhost if null"; + }; + port = mkOption { + type = natural'; + default = 10030; + description = "Tcp port to bind to"; + }; + }; + }; + + unixSocket = with types; { + options = { + path = mkOption { + type = path; + default = "/var/run/postgrey.sock"; + description = "Path of the unix socket"; + }; + + mode = mkOption { + type = string; + default = "0777"; + description = "Mode of the unix socket"; + }; + }; + }; + +in { + + options = { + services.postgrey = with types; { + enable = mkOption { + type = bool; + default = false; + description = "Whether to run the Postgrey daemon"; + }; + socket = mkOption { + type = socket; + default = { + path = "/var/run/postgrey.sock"; + mode = "0777"; + }; + example = { + addr = "127.0.0.1"; + port = 10030; + }; + description = "Socket to bind to"; + }; + greylistText = mkOption { + type = string; + default = "Greylisted for %%s seconds"; + description = "Response status text for greylisted messages; use %%s for seconds left until greylisting is over and %%r for mail domain of recipient"; + }; + greylistAction = mkOption { + type = string; + default = "DEFER_IF_PERMIT"; + description = "Response status for greylisted messages (see access(5))"; + }; + greylistHeader = mkOption { + type = string; + default = "X-Greylist: delayed %%t seconds by postgrey-%%v at %%h; %%d"; + description = "Prepend header to greylisted mails; use %%t for seconds delayed due to greylisting, %%v for the version of postgrey, %%d for the date, and %%h for the host"; + }; + delay = mkOption { + type = natural; + default = 300; + description = "Greylist for N seconds"; + }; + maxAge = mkOption { + type = natural; + default = 35; + description = "Delete entries from whitelist if they haven't been seen for N days"; + }; + retryWindow = mkOption { + type = either string natural; + default = 2; + example = "12h"; + description = "Allow N days for the first retry. Use string with appended 'h' to specify time in hours"; + }; + lookupBySubnet = mkOption { + type = bool; + default = true; + description = "Strip the last N bits from IP addresses, determined by IPv4CIDR and IPv6CIDR"; + }; + IPv4CIDR = mkOption { + type = natural; + default = 24; + description = "Strip N bits from IPv4 addresses if lookupBySubnet is true"; + }; + IPv6CIDR = mkOption { + type = natural; + default = 64; + description = "Strip N bits from IPv6 addresses if lookupBySubnet is true"; + }; + privacy = mkOption { + type = bool; + default = true; + description = "Store data using one-way hash functions (SHA1)"; + }; + autoWhitelist = mkOption { + type = nullOr natural'; + default = 5; + description = "Whitelist clients after successful delivery of N messages"; + }; + whitelistClients = mkOption { + type = listOf path; + default = []; + description = "Client address whitelist files (see postgrey(8))"; + }; + whitelistRecipients = mkOption { + type = listOf path; + default = []; + description = "Recipient address whitelist files (see postgrey(8))"; + }; + }; + }; + + config = mkIf cfg.enable { + + environment.systemPackages = [ pkgs.postgrey ]; + + users = { + extraUsers = { + postgrey = { + description = "Postgrey Daemon"; + uid = config.ids.uids.postgrey; + group = "postgrey"; + }; + }; + extraGroups = { + postgrey = { + gid = config.ids.gids.postgrey; + }; + }; + }; + + systemd.services.postgrey = let + bind-flag = if cfg.socket ? "path" then + ''--unix=${cfg.socket.path} --socketmode=${cfg.socket.mode}'' + else + ''--inet=${optionalString (cfg.socket.addr != null) (cfg.socket.addr + ":")}${toString cfg.socket.port}''; + in { + description = "Postfix Greylisting Service"; + wantedBy = [ "multi-user.target" ]; + before = [ "postfix.service" ]; + preStart = '' + mkdir -p /var/postgrey + chown postgrey:postgrey /var/postgrey + chmod 0770 /var/postgrey + ''; + serviceConfig = { + Type = "simple"; + ExecStart = ''${pkgs.postgrey}/bin/postgrey \ + ${bind-flag} \ + --group=postgrey --user=postgrey \ + --dbdir=/var/postgrey \ + --delay=${toString cfg.delay} \ + --max-age=${toString cfg.maxAge} \ + --retry-window=${toString cfg.retryWindow} \ + ${if cfg.lookupBySubnet then "--lookup-by-subnet" else "--lookup-by-host"} \ + --ipv4cidr=${toString cfg.IPv4CIDR} --ipv6cidr=${toString cfg.IPv6CIDR} \ + ${optionalString cfg.privacy "--privacy"} \ + --auto-whitelist-clients=${toString (if cfg.autoWhitelist == null then 0 else cfg.autoWhitelist)} \ + --greylist-action=${cfg.greylistAction} \ + --greylist-text="${cfg.greylistText}" \ + --x-greylist-header="${cfg.greylistHeader}" \ + ${concatMapStringsSep " " (x: "--whitelist-clients=" + x) cfg.whitelistClients} \ + ${concatMapStringsSep " " (x: "--whitelist-recipients=" + x) cfg.whitelistRecipients} + ''; + Restart = "always"; + RestartSec = 5; + TimeoutSec = 10; + }; + }; + + }; + +} diff --git a/nixos/modules/services/mail/postsrsd.nix b/nixos/modules/services/mail/postsrsd.nix index 68a4c101206..a1af16ec9ac 100644 --- a/nixos/modules/services/mail/postsrsd.nix +++ b/nixos/modules/services/mail/postsrsd.nix @@ -20,17 +20,29 @@ in { description = "Whether to enable the postsrsd SRS server for Postfix."; }; + secretsFile = mkOption { + type = types.path; + default = "/var/lib/postsrsd/postsrsd.secret"; + description = "Secret keys used for signing and verification"; + }; + domain = mkOption { type = types.str; description = "Domain name for rewrite"; }; - secretsFile = mkOption { - type = types.path; - default = "/var/lib/postsrsd/postsrsd.secret"; - description = "Secret keys used for signing and verification"; + separator = mkOption { + type = types.enum ["-" "=" "+"]; + default = "="; + description = "First separator character in generated addresses"; }; + # bindAddress = mkOption { # uncomment once 1.5 is released + # type = types.str; + # default = "127.0.0.1"; + # description = "Socket listen address"; + # }; + forwardPort = mkOption { type = types.int; default = 10001; @@ -43,6 +55,18 @@ in { description = "Port for the reverse SRS lookup"; }; + timeout = mkOption { + type = types.int; + default = 1800; + description = "Timeout for idle client connections in seconds"; + }; + + excludeDomains = mkOption { + type = types.listOf types.str; + default = []; + description = "Origin domains to exclude from rewriting in addition to primary domain"; + }; + user = mkOption { type = types.str; default = "postsrsd"; @@ -86,7 +110,7 @@ in { path = [ pkgs.coreutils ]; serviceConfig = { - ExecStart = ''${pkgs.postsrsd}/sbin/postsrsd "-s${cfg.secretsFile}" "-d${cfg.domain}" -f${toString cfg.forwardPort} -r${toString cfg.reversePort}''; + ExecStart = ''${pkgs.postsrsd}/sbin/postsrsd "-s${cfg.secretsFile}" "-d${cfg.domain}" -a${cfg.separator} -f${toString cfg.forwardPort} -r${toString cfg.reversePort} -t${toString cfg.timeout} "-X${concatStringsSep "," cfg.excludeDomains}"''; User = cfg.user; Group = cfg.group; PermissionsStartOnly = true; diff --git a/nixos/modules/services/mail/rmilter.nix b/nixos/modules/services/mail/rmilter.nix index e27b38bc0e2..8f18b929c11 100644 --- a/nixos/modules/services/mail/rmilter.nix +++ b/nixos/modules/services/mail/rmilter.nix @@ -203,7 +203,7 @@ milter_default_action = accept PermissionsStartOnly = true; Restart = "always"; RuntimeDirectory = "rmilter"; - RuntimeDirectoryPermissions="0755"; + RuntimeDirectoryMode = "0755"; }; }; diff --git a/nixos/modules/services/misc/apache-kafka.nix b/nixos/modules/services/misc/apache-kafka.nix index 88ce8b5a23f..c856d3294c0 100644 --- a/nixos/modules/services/misc/apache-kafka.nix +++ b/nixos/modules/services/misc/apache-kafka.nix @@ -139,7 +139,7 @@ in { systemd.services.apache-kafka = { description = "Apache Kafka Daemon"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; serviceConfig = { ExecStart = '' ${pkgs.jre}/bin/java \ diff --git a/nixos/modules/services/misc/autofs.nix b/nixos/modules/services/misc/autofs.nix index 18f0c3eb83d..40b48f70f7e 100644 --- a/nixos/modules/services/misc/autofs.nix +++ b/nixos/modules/services/misc/autofs.nix @@ -22,7 +22,7 @@ in default = false; description = " Mount filesystems on demand. Unmount them automatically. - You may also be interested in afuese. + You may also be interested in afuse. "; }; diff --git a/nixos/modules/services/misc/bepasty.nix b/nixos/modules/services/misc/bepasty.nix index 5bda73ab64f..52719222db6 100644 --- a/nixos/modules/services/misc/bepasty.nix +++ b/nixos/modules/services/misc/bepasty.nix @@ -53,7 +53,7 @@ in }; extraConfig = mkOption { - type = types.str; + type = types.lines; description = '' Extra configuration for bepasty server to be appended on the configuration. diff --git a/nixos/modules/services/misc/confd.nix b/nixos/modules/services/misc/confd.nix index 72ec68dee6b..fe13013286b 100644..100755 --- a/nixos/modules/services/misc/confd.nix +++ b/nixos/modules/services/misc/confd.nix @@ -33,7 +33,7 @@ in { nodes = mkOption { description = "Confd list of nodes to connect to."; - default = [ "http://127.0.0.1:4001" ]; + default = [ "http://127.0.0.1:2379" ]; type = types.listOf types.str; }; diff --git a/nixos/modules/services/misc/dictd.nix b/nixos/modules/services/misc/dictd.nix index ef744439c3d..7e3b6431a13 100644 --- a/nixos/modules/services/misc/dictd.nix +++ b/nixos/modules/services/misc/dictd.nix @@ -2,6 +2,10 @@ with lib; +let + cfg = config.services.dictd; +in + { ###### interface @@ -20,8 +24,9 @@ with lib; DBs = mkOption { type = types.listOf types.package; - default = []; - example = [ pkgs.dictdDBs.nld2eng ]; + default = with pkgs.dictdDBs; [ wiktionary wordnet ]; + defaultText = "with pkgs.dictdDBs; [ wiktionary wordnet ]"; + example = literalExample "[ pkgs.dictdDBs.nld2eng ]"; description = ''List of databases to make available.''; }; @@ -34,8 +39,8 @@ with lib; config = let dictdb = pkgs.dictDBCollector { dictlist = map (x: { name = x.name; - filename = x; } ) config.services.dictd.DBs; }; - in mkIf config.services.dictd.enable { + filename = x; } ) cfg.DBs; }; + in mkIf cfg.enable { # get the command line client on system path to make some use of the service environment.systemPackages = [ pkgs.dict ]; diff --git a/nixos/modules/services/misc/disnix.nix b/nixos/modules/services/misc/disnix.nix index e5a125ad324..e96645c79c7 100644 --- a/nixos/modules/services/misc/disnix.nix +++ b/nixos/modules/services/misc/disnix.nix @@ -41,6 +41,7 @@ in type = types.path; description = "The Disnix package"; default = pkgs.disnix; + defaultText = "pkgs.disnix"; }; }; diff --git a/nixos/modules/services/misc/docker-registry.nix b/nixos/modules/services/misc/docker-registry.nix index add339f9bdf..96ac2a1cf2c 100644 --- a/nixos/modules/services/misc/docker-registry.nix +++ b/nixos/modules/services/misc/docker-registry.nix @@ -6,14 +6,8 @@ let cfg = config.services.dockerRegistry; in { - ###### interface - options.services.dockerRegistry = { - enable = mkOption { - description = "Whether to enable docker registry server."; - default = false; - type = types.bool; - }; + enable = mkEnableOption "Docker Registry"; listenAddress = mkOption { description = "Docker registry host or ip to bind to."; @@ -35,8 +29,7 @@ in { extraConfig = mkOption { description = '' - Docker extra registry configuration. See - <link xlink:href="https://github.com/docker/docker-registry/blob/master/config/config_sample.yml"/> + Docker extra registry configuration via environment variables. ''; default = {}; type = types.attrsOf types.str; @@ -50,32 +43,24 @@ in { after = [ "network.target" ]; environment = { - REGISTRY_HOST = cfg.listenAddress; - REGISTRY_PORT = toString cfg.port; - GUNICORN_OPTS = "[--preload]"; # see https://github.com/docker/docker-registry#sqlalchemy - STORAGE_PATH = cfg.storagePath; + REGISTRY_HTTP_ADDR = "${cfg.listenAddress}:${toString cfg.port}"; + REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY = cfg.storagePath; } // cfg.extraConfig; + script = '' + ${pkgs.docker-distribution}/bin/registry serve \ + ${pkgs.docker-distribution.out}/share/go/src/github.com/docker/distribution/cmd/registry/config-example.yml + ''; + serviceConfig = { - ExecStart = "${pkgs.pythonPackages.docker_registry}/bin/docker-registry"; User = "docker-registry"; - Group = "docker"; - PermissionsStartOnly = true; WorkingDirectory = cfg.storagePath; }; - - postStart = '' - until ${pkgs.curl.bin}/bin/curl -s -o /dev/null 'http://${cfg.listenAddress}:${toString cfg.port}/'; do - sleep 1; - done - ''; }; - users.extraGroups.docker.gid = mkDefault config.ids.gids.docker; users.extraUsers.docker-registry = { createHome = true; home = cfg.storagePath; - uid = config.ids.uids.docker-registry; }; }; } diff --git a/nixos/modules/services/misc/emby.nix b/nixos/modules/services/misc/emby.nix index fe872349f45..9f290ed70c9 100644 --- a/nixos/modules/services/misc/emby.nix +++ b/nixos/modules/services/misc/emby.nix @@ -43,7 +43,7 @@ in User = cfg.user; Group = cfg.group; PermissionsStartOnly = "true"; - ExecStart = "${pkgs.mono}/bin/mono ${pkgs.emby}/bin/MediaBrowser.Server.Mono.exe"; + ExecStart = "${pkgs.emby}/bin/MediaBrowser.Server.Mono"; Restart = "on-failure"; }; }; diff --git a/nixos/modules/services/misc/errbot.nix b/nixos/modules/services/misc/errbot.nix new file mode 100644 index 00000000000..427cb7c546d --- /dev/null +++ b/nixos/modules/services/misc/errbot.nix @@ -0,0 +1,101 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.errbot; + pluginEnv = plugins: pkgs.buildEnv { + name = "errbot-plugins"; + paths = plugins; + }; + mkConfigDir = instanceCfg: dataDir: pkgs.writeTextDir "config.py" '' + import logging + BACKEND = '${instanceCfg.backend}' + BOT_DATA_DIR = '${dataDir}' + BOT_EXTRA_PLUGIN_DIR = '${pluginEnv instanceCfg.plugins}' + + BOT_LOG_LEVEL = logging.${instanceCfg.logLevel} + BOT_LOG_FILE = False + + BOT_ADMINS = (${concatMapStringsSep "," (name: "'${name}'") instanceCfg.admins}) + + BOT_IDENTITY = ${builtins.toJSON instanceCfg.identity} + + ${instanceCfg.extraConfig} + ''; +in { + options = { + services.errbot.instances = mkOption { + default = {}; + description = "Errbot instance configs"; + type = types.attrsOf (types.submodule { + options = { + dataDir = mkOption { + type = types.nullOr types.path; + default = null; + description = "Data directory for errbot instance."; + }; + + plugins = mkOption { + type = types.listOf types.package; + default = []; + description = "List of errbot plugin derivations."; + }; + + logLevel = mkOption { + type = types.str; + default = "INFO"; + description = "Errbot log level"; + }; + + admins = mkOption { + type = types.listOf types.str; + default = []; + description = "List of identifiers of errbot admins."; + }; + + backend = mkOption { + type = types.str; + default = "XMPP"; + description = "Errbot backend name."; + }; + + identity = mkOption { + type = types.attrs; + description = "Errbot identity configuration"; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = "String to be appended to the config verbatim"; + }; + }; + }); + }; + }; + + config = mkIf (cfg.instances != {}) { + users.extraUsers.errbot.group = "errbot"; + users.extraGroups.errbot = {}; + + systemd.services = mapAttrs' (name: instanceCfg: nameValuePair "errbot-${name}" ( + let + dataDir = if !isNull instanceCfg.dataDir then instanceCfg.dataDir else + "/var/lib/errbot/${name}"; + in { + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + preStart = '' + mkdir -p ${dataDir} + chown -R errbot:errbot ${dataDir} + ''; + serviceConfig = { + User = "errbot"; + Restart = "on-failure"; + ExecStart = "${pkgs.errbot}/bin/errbot -c ${mkConfigDir instanceCfg dataDir}/config.py"; + PermissionsStartOnly = true; + }; + })) cfg.instances; + }; +} diff --git a/nixos/modules/services/misc/etcd.nix b/nixos/modules/services/misc/etcd.nix index 0d6ed8eb904..1de02d76ba0 100644 --- a/nixos/modules/services/misc/etcd.nix +++ b/nixos/modules/services/misc/etcd.nix @@ -28,13 +28,13 @@ in { listenClientUrls = mkOption { description = "Etcd list of URLs to listen on for client traffic."; - default = ["http://localhost:4001"]; + default = ["http://127.0.0.1:2379"]; type = types.listOf types.str; }; listenPeerUrls = mkOption { description = "Etcd list of URLs to listen on for peer traffic."; - default = ["http://localhost:7001"]; + default = ["http://127.0.0.1:2380"]; type = types.listOf types.str; }; @@ -46,7 +46,7 @@ in { initialCluster = mkOption { description = "Etcd initial cluster configuration for bootstrapping."; - default = ["${cfg.name}=http://localhost:7001"]; + default = ["${cfg.name}=http://127.0.0.1:2380"]; type = types.listOf types.str; }; @@ -68,6 +68,54 @@ in { type = types.str; }; + clientCertAuth = mkOption { + description = "Whether to use certs for client authentication"; + default = false; + type = types.bool; + }; + + trustedCaFile = mkOption { + description = "Certificate authority file to use for clients"; + default = null; + type = types.nullOr types.path; + }; + + certFile = mkOption { + description = "Cert file to use for clients"; + default = null; + type = types.nullOr types.path; + }; + + keyFile = mkOption { + description = "Key file to use for clients"; + default = null; + type = types.nullOr types.path; + }; + + peerCertFile = mkOption { + description = "Cert file to use for peer to peer communication"; + default = cfg.certFile; + type = types.nullOr types.path; + }; + + peerKeyFile = mkOption { + description = "Key file to use for peer to peer communication"; + default = cfg.keyFile; + type = types.nullOr types.path; + }; + + peerTrustedCaFile = mkOption { + description = "Certificate authority file to use for peer to peer communication"; + default = cfg.trustedCaFile; + type = types.nullOr types.path; + }; + + peerClientCertAuth = mkOption { + description = "Whether to check all incoming peer requests from the cluster for valid client certificates signed by the supplied CA"; + default = false; + type = types.bool; + }; + extraConf = mkOption { description = '' Etcd extra configuration. See @@ -95,11 +143,11 @@ in { config = mkIf cfg.enable { systemd.services.etcd = { - description = "Etcd Daemon"; + description = "etcd key-value store"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; - environment = { + environment = (filterAttrs (n: v: v != null) { ETCD_NAME = cfg.name; ETCD_DISCOVERY = cfg.discovery; ETCD_DATA_DIR = cfg.dataDir; @@ -107,18 +155,31 @@ in { ETCD_LISTEN_CLIENT_URLS = concatStringsSep "," cfg.listenClientUrls; ETCD_LISTEN_PEER_URLS = concatStringsSep "," cfg.listenPeerUrls; ETCD_INITIAL_ADVERTISE_PEER_URLS = concatStringsSep "," cfg.initialAdvertisePeerUrls; - } // (optionalAttrs (cfg.discovery == ""){ + ETCD_PEER_TRUSTED_CA_FILE = cfg.peerTrustedCaFile; + ETCD_PEER_CERT_FILE = cfg.peerCertFile; + ETCD_PEER_KEY_FILE = cfg.peerKeyFile; + ETCD_CLIENT_CERT_AUTH = toString cfg.peerClientCertAuth; + ETCD_TRUSTED_CA_FILE = cfg.trustedCaFile; + ETCD_CERT_FILE = cfg.certFile; + ETCD_KEY_FILE = cfg.keyFile; + }) // (optionalAttrs (cfg.discovery == ""){ ETCD_INITIAL_CLUSTER = concatStringsSep "," cfg.initialCluster; ETCD_INITIAL_CLUSTER_STATE = cfg.initialClusterState; ETCD_INITIAL_CLUSTER_TOKEN = cfg.initialClusterToken; }) // (mapAttrs' (n: v: nameValuePair "ETCD_${n}" v) cfg.extraConf); + unitConfig = { + Documentation = "https://github.com/coreos/etcd"; + }; + serviceConfig = { Type = "notify"; ExecStart = "${pkgs.etcd.bin}/bin/etcd"; User = "etcd"; PermissionsStartOnly = true; + LimitNOFILE = 40000; }; + preStart = '' mkdir -m 0700 -p ${cfg.dataDir} if [ "$(id -u)" = 0 ]; then chown etcd ${cfg.dataDir}; fi diff --git a/nixos/modules/services/misc/folding-at-home.nix b/nixos/modules/services/misc/folding-at-home.nix index 4f09cbfdd79..053e7e95635 100644 --- a/nixos/modules/services/misc/folding-at-home.nix +++ b/nixos/modules/services/misc/folding-at-home.nix @@ -50,7 +50,7 @@ in { }; systemd.services.foldingathome = { - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; preStart = '' mkdir -m 0755 -p ${stateDir} diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix index b3f09999adb..1fc3a5cc869 100644 --- a/nixos/modules/services/misc/gitlab.nix +++ b/nixos/modules/services/misc/gitlab.nix @@ -164,18 +164,21 @@ in { packages.gitlab = mkOption { type = types.package; default = pkgs.gitlab; + defaultText = "pkgs.gitlab"; description = "Reference to the gitlab package"; }; packages.gitlab-shell = mkOption { type = types.package; default = pkgs.gitlab-shell; + defaultText = "pkgs.gitlab-shell"; description = "Reference to the gitlab-shell package"; }; packages.gitlab-workhorse = mkOption { type = types.package; default = pkgs.gitlab-workhorse; + defaultText = "pkgs.gitlab-workhorse"; description = "Reference to the gitlab-workhorse package"; }; @@ -425,7 +428,7 @@ in { TimeoutSec = "300"; Restart = "on-failure"; WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab"; - ExecStart="${cfg.packages.gitlab.env}/bin/bundle exec \"sidekiq -q post_receive -q mailers -q system_hook -q project_web_hook -q gitlab_shell -q common -q default -e production -P ${cfg.statePath}/tmp/sidekiq.pid\""; + ExecStart="${cfg.packages.gitlab.env}/bin/bundle exec \"sidekiq -C \"${cfg.packages.gitlab}/share/gitlab/config/sidekiq_queues.yml\" -e production -P ${cfg.statePath}/tmp/sidekiq.pid\""; }; }; @@ -449,18 +452,21 @@ in { Group = cfg.group; TimeoutSec = "300"; Restart = "on-failure"; + WorkingDirectory = gitlabEnv.HOME; ExecStart = "${cfg.packages.gitlab-workhorse}/bin/gitlab-workhorse " + "-listenUmask 0 " + "-listenNetwork unix " + "-listenAddr /run/gitlab/gitlab-workhorse.socket " + "-authSocket ${gitlabSocket} " - + "-documentRoot ${cfg.packages.gitlab}/share/gitlab/public"; + + "-documentRoot ${cfg.packages.gitlab}/share/gitlab/public " + + "-secretPath ${cfg.packages.gitlab}/share/gitlab/.gitlab_workhorse_secret"; }; }; systemd.services.gitlab = { after = [ "network.target" "postgresql.service" "redis.service" ]; + requires = [ "gitlab-sidekiq.service" ]; wantedBy = [ "multi-user.target" ]; environment = gitlabEnv; path = with pkgs; [ @@ -525,17 +531,23 @@ in { psql postgres -c "CREATE ROLE gitlab WITH LOGIN NOCREATEDB NOCREATEROLE NOCREATEUSER ENCRYPTED PASSWORD '${cfg.databasePassword}'" ${config.services.postgresql.package}/bin/createdb --owner gitlab gitlab || true touch "${cfg.statePath}/db-created" - - # The gitlab:setup task is horribly broken somehow, these two tasks will do the same for setting up the initial database - ${gitlab-rake}/bin/gitlab-rake db:migrate RAILS_ENV=production - ${gitlab-rake}/bin/gitlab-rake db:seed_fu RAILS_ENV=production \ - GITLAB_ROOT_PASSWORD="${cfg.initialRootPassword}" GITLAB_ROOT_EMAIL="${cfg.initialRootEmail}"; fi fi + # enable required pg_trgm extension for gitlab + psql gitlab -c "CREATE EXTENSION IF NOT EXISTS pg_trgm" # Always do the db migrations just to be sure the database is up-to-date ${gitlab-rake}/bin/gitlab-rake db:migrate RAILS_ENV=production + # The gitlab:setup task is horribly broken somehow, the db:migrate + # task above and the db:seed_fu below will do the same for setting + # up the initial database + if ! test -e "${cfg.statePath}/db-seeded"; then + ${gitlab-rake}/bin/gitlab-rake db:seed_fu RAILS_ENV=production \ + GITLAB_ROOT_PASSWORD="${cfg.initialRootPassword}" GITLAB_ROOT_EMAIL="${cfg.initialRootEmail}" + touch "${cfg.statePath}/db-seeded" + fi + # Change permissions in the last step because some of the # intermediary scripts like to create directories as root. chown -R ${cfg.user}:${cfg.group} ${cfg.statePath} diff --git a/nixos/modules/services/misc/leaps.nix b/nixos/modules/services/misc/leaps.nix new file mode 100644 index 00000000000..b92cf27f58d --- /dev/null +++ b/nixos/modules/services/misc/leaps.nix @@ -0,0 +1,62 @@ +{ config, pkgs, lib, ... } @ args: + +with lib; + +let + cfg = config.services.leaps; + stateDir = "/var/lib/leaps/"; +in +{ + options = { + services.leaps = { + enable = mkEnableOption "leaps"; + port = mkOption { + type = types.int; + default = 8080; + description = "A port where leaps listens for incoming http requests"; + }; + address = mkOption { + default = ""; + type = types.str; + example = "127.0.0.1"; + description = "Hostname or IP-address to listen to. By default it will listen on all interfaces."; + }; + path = mkOption { + default = "/"; + type = types.path; + description = "Subdirectory used for reverse proxy setups"; + }; + }; + }; + + config = mkIf cfg.enable { + users = { + users.leaps = { + uid = config.ids.uids.leaps; + description = "Leaps server user"; + group = "leaps"; + home = stateDir; + createHome = true; + }; + + groups.leaps = { + gid = config.ids.gids.leaps; + }; + }; + + systemd.services.leaps = { + description = "leaps service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + User = "leaps"; + Group = "leaps"; + Restart = "on-failure"; + WorkingDirectory = stateDir; + PrivateTmp = true; + ExecStart = "${pkgs.leaps.bin}/bin/leaps -path ${toString cfg.path} -address ${cfg.address}:${toString cfg.port}"; + }; + }; + }; +} diff --git a/nixos/modules/services/misc/matrix-synapse.nix b/nixos/modules/services/misc/matrix-synapse.nix index bb8dc640f98..4a1bea50c14 100644 --- a/nixos/modules/services/misc/matrix-synapse.nix +++ b/nixos/modules/services/misc/matrix-synapse.nix @@ -5,15 +5,20 @@ with lib; let cfg = config.services.matrix-synapse; logConfigFile = pkgs.writeText "log_config.yaml" cfg.logConfig; - mkResource = r: ''{names: ${builtins.toJSON r.names}, compress: ${if r.compress then "true" else "false"}}''; - mkListener = l: ''{port: ${toString l.port}, bind_address: "${l.bind_address}", type: ${l.type}, tls: ${if l.tls then "true" else "false"}, x_forwarded: ${if l.x_forwarded then "true" else "false"}, resources: [${concatStringsSep "," (map mkResource l.resources)}]}''; + mkResource = r: ''{names: ${builtins.toJSON r.names}, compress: ${fromBool r.compress}}''; + mkListener = l: ''{port: ${toString l.port}, bind_address: "${l.bind_address}", type: ${l.type}, tls: ${fromBool l.tls}, x_forwarded: ${fromBool l.x_forwarded}, resources: [${concatStringsSep "," (map mkResource l.resources)}]}''; + fromBool = x: if x then "true" else "false"; configFile = pkgs.writeText "homeserver.yaml" '' +${optionalString (cfg.tls_certificate_path != null) '' tls_certificate_path: "${cfg.tls_certificate_path}" +''} ${optionalString (cfg.tls_private_key_path != null) '' tls_private_key_path: "${cfg.tls_private_key_path}" ''} +${optionalString (cfg.tls_dh_params_path != null) '' tls_dh_params_path: "${cfg.tls_dh_params_path}" -no_tls: ${if cfg.no_tls then "true" else "false"} +''} +no_tls: ${fromBool cfg.no_tls} ${optionalString (cfg.bind_port != null) '' bind_port: ${toString cfg.bind_port} ''} @@ -25,7 +30,7 @@ bind_host: "${cfg.bind_host}" ''} server_name: "${cfg.server_name}" pid_file: "/var/run/matrix-synapse.pid" -web_client: ${if cfg.web_client then "true" else "false"} +web_client: ${fromBool cfg.web_client} ${optionalString (cfg.public_baseurl != null) '' public_baseurl: "${cfg.public_baseurl}" ''} @@ -53,14 +58,19 @@ media_store_path: "/var/lib/matrix-synapse/media" uploads_path: "/var/lib/matrix-synapse/uploads" max_upload_size: "${cfg.max_upload_size}" max_image_pixels: "${cfg.max_image_pixels}" -dynamic_thumbnails: ${if cfg.dynamic_thumbnails then "true" else "false"} -url_preview_enabled: False +dynamic_thumbnails: ${fromBool cfg.dynamic_thumbnails} +url_preview_enabled: ${fromBool cfg.url_preview_enabled} +${optionalString (cfg.url_preview_enabled == true) '' +url_preview_ip_range_blacklist: ${builtins.toJSON cfg.url_preview_ip_range_blacklist} +url_preview_ip_range_whitelist: ${builtins.toJSON cfg.url_preview_ip_range_whitelist} +url_preview_url_blacklist: ${builtins.toJSON cfg.url_preview_url_blacklist} +''} recaptcha_private_key: "${cfg.recaptcha_private_key}" recaptcha_public_key: "${cfg.recaptcha_public_key}" -enable_registration_captcha: ${if cfg.enable_registration_captcha then "true" else "false"} +enable_registration_captcha: ${fromBool cfg.enable_registration_captcha} turn_uris: ${builtins.toJSON cfg.turn_uris} turn_shared_secret: "${cfg.turn_shared_secret}" -enable_registration: ${if cfg.enable_registration then "true" else "false"} +enable_registration: ${fromBool cfg.enable_registration} ${optionalString (cfg.registration_shared_secret != null) '' registration_shared_secret: "${cfg.registration_shared_secret}" ''} @@ -68,9 +78,15 @@ recaptcha_siteverify_api: "https://www.google.com/recaptcha/api/siteverify" turn_user_lifetime: "${cfg.turn_user_lifetime}" user_creation_max_duration: ${cfg.user_creation_max_duration} bcrypt_rounds: ${cfg.bcrypt_rounds} -allow_guest_access: {if cfg.allow_guest_access then "true" else "false"} -enable_metrics: ${if cfg.enable_metrics then "true" else "false"} -report_stats: ${if cfg.report_stats then "true" else "false"} +allow_guest_access: ${fromBool cfg.allow_guest_access} +trusted_third_party_id_servers: ${builtins.toJSON cfg.trusted_third_party_id_servers} +room_invite_state_types: ${builtins.toJSON cfg.room_invite_state_types} +${optionalString (cfg.macaroon_secret_key != null) '' + macaroon_secret_key: "${cfg.macaroon_secret_key}" +''} +expire_access_token: ${fromBool cfg.expire_access_token} +enable_metrics: ${fromBool cfg.enable_metrics} +report_stats: ${fromBool cfg.report_stats} signing_key_path: "/var/lib/matrix-synapse/homeserver.signing.key" key_refresh_interval: "${cfg.key_refresh_interval}" perspectives: @@ -139,8 +155,9 @@ in { ''; }; tls_certificate_path = mkOption { - type = types.str; - default = "/var/lib/matrix-synapse/homeserver.tls.crt"; + type = types.nullOr types.str; + default = null; + example = "/var/lib/matrix-synapse/homeserver.tls.crt"; description = '' PEM encoded X509 certificate for TLS. You can replace the self-signed certificate that synapse @@ -151,16 +168,17 @@ in { }; tls_private_key_path = mkOption { type = types.nullOr types.str; - default = "/var/lib/matrix-synapse/homeserver.tls.key"; - example = null; + default = null; + example = "/var/lib/matrix-synapse/homeserver.tls.key"; description = '' PEM encoded private key for TLS. Specify null if synapse is not speaking TLS directly. ''; }; tls_dh_params_path = mkOption { - type = types.str; - default = "/var/lib/matrix-synapse/homeserver.tls.dh"; + type = types.nullOr types.str; + default = null; + example = "/var/lib/matrix-synapse/homeserver.tls.dh"; description = '' PEM dh parameters for ephemeral keys ''; @@ -342,6 +360,47 @@ in { default = "10K"; description = "Number of events to cache in memory."; }; + url_preview_enabled = mkOption { + type = types.bool; + default = false; + description = '' + Is the preview URL API enabled? If enabled, you *must* specify an + explicit url_preview_ip_range_blacklist of IPs that the spider is + denied from accessing. + ''; + }; + url_preview_ip_range_blacklist = mkOption { + type = types.listOf types.str; + default = []; + description = '' + List of IP address CIDR ranges that the URL preview spider is denied + from accessing. + ''; + }; + url_preview_ip_range_whitelist = mkOption { + type = types.listOf types.str; + default = []; + description = '' + List of IP address CIDR ranges that the URL preview spider is allowed + to access even if they are specified in + url_preview_ip_range_blacklist. + ''; + }; + url_preview_url_blacklist = mkOption { + type = types.listOf types.str; + default = [ + "127.0.0.0/8" + "10.0.0.0/8" + "172.16.0.0/12" + "192.168.0.0/16" + "100.64.0.0/10" + "169.254.0.0/16" + ]; + description = '' + Optional list of URL matches that the URL preview spider is + denied from accessing. + ''; + }; recaptcha_private_key = mkOption { type = types.str; default = ""; @@ -469,6 +528,34 @@ in { accessible to anonymous users. ''; }; + trusted_third_party_id_servers = mkOption { + type = types.listOf types.str; + default = ["matrix.org"]; + description = '' + The list of identity servers trusted to verify third party identifiers by this server. + ''; + }; + room_invite_state_types = mkOption { + type = types.listOf types.str; + default = ["m.room.join_rules" "m.room.canonical_alias" "m.room.avatar" "m.room.name"]; + description = '' + A list of event types that will be included in the room_invite_state + ''; + }; + macaroon_secret_key = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Secret key for authentication tokens + ''; + }; + expire_access_token = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable access token expiration. + ''; + }; key_refresh_interval = mkOption { type = types.str; default = "1d"; @@ -522,12 +609,10 @@ in { after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; preStart = '' - if ! test -e /var/lib/matrix-synapse; then - mkdir -p /var/lib/matrix-synapse - chmod 700 /var/lib/matrix-synapse - chown -R matrix-synapse:matrix-synapse /var/lib/matrix-synapse - ${cfg.package}/bin/homeserver --config-path ${configFile} --keys-directory /var/lib/matrix-synapse/ --generate-keys - fi + ${cfg.package}/bin/homeserver \ + --config-path ${configFile} \ + --keys-directory /var/lib/matrix-synapse \ + --generate-keys ''; serviceConfig = { Type = "simple"; @@ -535,7 +620,7 @@ in { Group = "matrix-synapse"; WorkingDirectory = "/var/lib/matrix-synapse"; PermissionsStartOnly = true; - ExecStart = "${cfg.package}/bin/homeserver --config-path ${configFile}"; + ExecStart = "${cfg.package}/bin/homeserver --config-path ${configFile} --keys-directory /var/lib/matrix-synapse"; }; }; }; diff --git a/nixos/modules/services/misc/mesos-master.nix b/nixos/modules/services/misc/mesos-master.nix index 497646b2b41..99583ebeebd 100644 --- a/nixos/modules/services/misc/mesos-master.nix +++ b/nixos/modules/services/misc/mesos-master.nix @@ -80,7 +80,7 @@ in { systemd.services.mesos-master = { description = "Mesos Master"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; serviceConfig = { ExecStart = '' ${pkgs.mesos}/bin/mesos-master \ diff --git a/nixos/modules/services/misc/mesos-slave.nix b/nixos/modules/services/misc/mesos-slave.nix index 8c29734813a..9ddecb6fe30 100644 --- a/nixos/modules/services/misc/mesos-slave.nix +++ b/nixos/modules/services/misc/mesos-slave.nix @@ -105,7 +105,7 @@ in { systemd.services.mesos-slave = { description = "Mesos Slave"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; environment.MESOS_CONTAINERIZERS = concatStringsSep "," containerizers; serviceConfig = { ExecStart = '' diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix index 333782d15bc..7101cadfeed 100644 --- a/nixos/modules/services/misc/nix-daemon.nix +++ b/nixos/modules/services/misc/nix-daemon.nix @@ -105,7 +105,9 @@ in If set, Nix will perform builds in a sandboxed environment that it will set up automatically for each build. This prevents impurities in builds by disallowing access to dependencies - outside of the Nix store. + outside of the Nix store. This isn't enabled by default for + performance. It doesn't affect derivation hashes, so changing + this option will not trigger a rebuild of packages. "; }; @@ -172,8 +174,8 @@ in sshKey = "/root/.ssh/id_buildfarm"; system = "x86_64-linux"; maxJobs = 2; - supportedFeatures = "kvm"; - mandatoryFeatures = "perf"; + supportedFeatures = [ "kvm" ]; + mandatoryFeatures = [ "perf" ]; } ]; description = '' diff --git a/nixos/modules/services/misc/nix-gc.nix b/nixos/modules/services/misc/nix-gc.nix index 5c13da6e83d..304168c65b0 100644 --- a/nixos/modules/services/misc/nix-gc.nix +++ b/nixos/modules/services/misc/nix-gc.nix @@ -53,7 +53,7 @@ in systemd.services.nix-gc = { description = "Nix Garbage Collector"; script = "exec ${config.nix.package.out}/bin/nix-collect-garbage ${cfg.options}"; - startAt = optionalString cfg.automatic cfg.dates; + startAt = optional cfg.automatic cfg.dates; }; }; diff --git a/nixos/modules/services/misc/nix-optimise.nix b/nixos/modules/services/misc/nix-optimise.nix new file mode 100644 index 00000000000..a76bfd9f1f1 --- /dev/null +++ b/nixos/modules/services/misc/nix-optimise.nix @@ -0,0 +1,49 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.nix.optimise; +in + +{ + + ###### interface + + options = { + + nix.optimise = { + + automatic = mkOption { + default = false; + type = types.bool; + description = "Automatically run the nix store optimiser at a specific time."; + }; + + dates = mkOption { + default = ["03:45"]; + type = types.listOf types.str; + description = '' + Specification (in the format described by + <citerefentry><refentrytitle>systemd.time</refentrytitle> + <manvolnum>5</manvolnum></citerefentry>) of the time at + which the optimiser will run. + ''; + }; + }; + }; + + + ###### implementation + + config = { + + systemd.services.nix-optimise = + { description = "Nix Store Optimiser"; + serviceConfig.ExecStart = "${config.nix.package}/bin/nix-store --optimise"; + startAt = optionals cfg.automatic cfg.dates; + }; + + }; + +} diff --git a/nixos/modules/services/misc/nscd-sssd.conf b/nixos/modules/services/misc/nscd-sssd.conf new file mode 100644 index 00000000000..92380f3e4ba --- /dev/null +++ b/nixos/modules/services/misc/nscd-sssd.conf @@ -0,0 +1,36 @@ +server-user nscd +threads 1 +paranoia no +debug-level 0 + +enable-cache passwd yes +positive-time-to-live passwd 0 +negative-time-to-live passwd 0 +suggested-size passwd 211 +check-files passwd yes +persistent passwd no +shared passwd yes + +enable-cache group yes +positive-time-to-live group 0 +negative-time-to-live group 0 +suggested-size group 211 +check-files group yes +persistent group no +shared group yes + +enable-cache hosts yes +positive-time-to-live hosts 600 +negative-time-to-live hosts 5 +suggested-size hosts 211 +check-files hosts yes +persistent hosts no +shared hosts yes + +enable-cache services yes +positive-time-to-live services 0 +negative-time-to-live services 0 +suggested-size services 211 +check-files services yes +persistent services no +shared services yes diff --git a/nixos/modules/services/misc/parsoid.nix b/nixos/modules/services/misc/parsoid.nix index 0844190a549..ae3f84333d2 100644 --- a/nixos/modules/services/misc/parsoid.nix +++ b/nixos/modules/services/misc/parsoid.nix @@ -6,20 +6,21 @@ let cfg = config.services.parsoid; - conf = '' - exports.setup = function( parsoidConfig ) { - ${toString (mapAttrsToList (name: str: "parsoidConfig.setInterwiki('${name}', '${str}');") cfg.interwikis)} - - parsoidConfig.serverInterface = "${cfg.interface}"; - parsoidConfig.serverPort = ${toString cfg.port}; - - parsoidConfig.useSelser = true; - - ${cfg.extraConfig} - }; - ''; + confTree = { + worker_heartbeat_timeout = 300000; + logging = { level = "info"; }; + services = [{ + module = "lib/index.js"; + entrypoint = "apiServiceWorker"; + conf = { + mwApis = map (x: if isAttrs x then x else { uri = x; }) cfg.wikis; + serverInterface = cfg.interface; + serverPort = cfg.port; + }; + }]; + }; - confFile = builtins.toFile "localsettings.js" conf; + confFile = pkgs.writeText "config.yml" (builtins.toJSON (recursiveUpdate confTree cfg.extraConfig)); in { @@ -38,9 +39,9 @@ in ''; }; - interwikis = mkOption { - type = types.attrsOf types.str; - example = { localhost = "http://localhost/api.php"; }; + wikis = mkOption { + type = types.listOf (types.either types.str types.attrs); + example = [ "http://localhost/api.php" ]; description = '' Used MediaWiki API endpoints. ''; @@ -71,8 +72,8 @@ in }; extraConfig = mkOption { - type = types.lines; - default = ""; + type = types.attrs; + default = {}; description = '' Extra configuration to add to parsoid configuration. ''; @@ -91,7 +92,8 @@ in wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; serviceConfig = { - ExecStart = "${pkgs.nodePackages.parsoid}/lib/node_modules/parsoid/api/server.js -c ${confFile} -n ${toString cfg.workers}"; + User = "nobody"; + ExecStart = "${pkgs.nodePackages.parsoid}/lib/node_modules/parsoid/bin/server.js -c ${confFile} -n ${toString cfg.workers}"; }; }; diff --git a/nixos/modules/services/misc/plex.nix b/nixos/modules/services/misc/plex.nix index 92b352db416..f6bf2dee986 100644 --- a/nixos/modules/services/misc/plex.nix +++ b/nixos/modules/services/misc/plex.nix @@ -19,6 +19,14 @@ in description = "The directory where Plex stores its data files."; }; + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Open ports in the firewall for the media server + ''; + }; + user = mkOption { type = types.str; default = "plex"; @@ -127,7 +135,7 @@ in User = cfg.user; Group = cfg.group; PermissionsStartOnly = "true"; - ExecStart = "/bin/sh -c '${cfg.package}/usr/lib/plexmediaserver/Plex\\ Media\\ Server'"; + ExecStart = "/bin/sh -c ${cfg.package}/usr/lib/plexmediaserver/Plex\\ Media\\ Server"; Restart = "on-failure"; }; environment = { @@ -141,6 +149,11 @@ in }; }; + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ 32400 3005 8324 32469 ]; + allowedUDPPorts = [ 1900 5353 32410 32412 32413 32414 ]; + }; + users.extraUsers = mkIf (cfg.user == "plex") { plex = { group = cfg.group; diff --git a/nixos/modules/services/misc/redmine.nix b/nixos/modules/services/misc/redmine.nix index 7c9483911f2..e3f1ec67cbb 100644 --- a/nixos/modules/services/misc/redmine.nix +++ b/nixos/modules/services/misc/redmine.nix @@ -71,7 +71,7 @@ in { }; extraConfig = mkOption { - type = types.str; + type = types.lines; default = ""; description = "Extra configuration in configuration.yml"; }; diff --git a/nixos/modules/services/misc/rippled.nix b/nixos/modules/services/misc/rippled.nix index c6b67e8498c..8bcf35a8ad3 100644 --- a/nixos/modules/services/misc/rippled.nix +++ b/nixos/modules/services/misc/rippled.nix @@ -154,43 +154,45 @@ let }; dbOptions = { - type = mkOption { - description = "Rippled database type."; - type = types.enum ["rocksdb" "nudb"]; - default = "rocksdb"; - }; + options = { + type = mkOption { + description = "Rippled database type."; + type = types.enum ["rocksdb" "nudb"]; + default = "rocksdb"; + }; - path = mkOption { - description = "Location to store the database."; - type = types.path; - default = cfg.databasePath; - }; + path = mkOption { + description = "Location to store the database."; + type = types.path; + default = cfg.databasePath; + }; - compression = mkOption { - description = "Whether to enable snappy compression."; - type = types.nullOr types.bool; - default = null; - }; + compression = mkOption { + description = "Whether to enable snappy compression."; + type = types.nullOr types.bool; + default = null; + }; - onlineDelete = mkOption { - description = "Enable automatic purging of older ledger information."; - type = types.addCheck (types.nullOr types.int) (v: v > 256); - default = cfg.ledgerHistory; - }; + onlineDelete = mkOption { + description = "Enable automatic purging of older ledger information."; + type = types.addCheck (types.nullOr types.int) (v: v > 256); + default = cfg.ledgerHistory; + }; - advisoryDelete = mkOption { - description = '' - If set, then require administrative RPC call "can_delete" - to enable online deletion of ledger records. - ''; - type = types.nullOr types.bool; - default = null; - }; + advisoryDelete = mkOption { + description = '' + If set, then require administrative RPC call "can_delete" + to enable online deletion of ledger records. + ''; + type = types.nullOr types.bool; + default = null; + }; - extraOpts = mkOption { - description = "Extra database options."; - type = types.lines; - default = ""; + extraOpts = mkOption { + description = "Extra database options."; + type = types.lines; + default = ""; + }; }; }; @@ -213,8 +215,7 @@ in ports = mkOption { description = "Ports exposed by rippled"; - type = types.attrsOf types.optionSet; - options = [portOptions]; + type = with types; attrsOf (submodule portOptions); default = { rpc = { port = 5005; @@ -238,8 +239,7 @@ in nodeDb = mkOption { description = "Rippled main database options."; - type = types.nullOr types.optionSet; - options = dbOptions; + type = with types; nullOr (submodule dbOptions); default = { type = "rocksdb"; extraOpts = '' @@ -254,15 +254,13 @@ in tempDb = mkOption { description = "Rippled temporary database options."; - type = types.nullOr types.optionSet; - options = dbOptions; + type = with types; nullOr (submodule dbOptions); default = null; }; importDb = mkOption { description = "Settings for performing a one-time import."; - type = types.nullOr types.optionSet; - options = dbOptions; + type = with types; nullOr (submodule dbOptions); default = null; }; diff --git a/nixos/modules/services/misc/sssd.nix b/nixos/modules/services/misc/sssd.nix new file mode 100644 index 00000000000..e818f4a4804 --- /dev/null +++ b/nixos/modules/services/misc/sssd.nix @@ -0,0 +1,97 @@ +{ config, lib, pkgs, ... }: +with lib; +let + cfg = config.services.sssd; + nscd = config.services.nscd; +in { + options = { + services.sssd = { + enable = mkEnableOption "the System Security Services Daemon."; + + config = mkOption { + type = types.lines; + description = "Contents of <filename>sssd.conf</filename>."; + default = '' + [sssd] + config_file_version = 2 + services = nss, pam + domains = shadowutils + + [nss] + + [pam] + + [domain/shadowutils] + id_provider = proxy + proxy_lib_name = files + auth_provider = proxy + proxy_pam_target = sssd-shadowutils + proxy_fast_alias = True + ''; + }; + + sshAuthorizedKeysIntegration = mkOption { + type = types.bool; + default = false; + description = '' + Whether to make sshd look up authorized keys from SSS. + For this to work, the <literal>ssh</literal> SSS service must be enabled in the sssd configuration. + ''; + }; + }; + }; + config = mkMerge [ + (mkIf cfg.enable { + assertions = singleton { + assertion = nscd.enable; + message = "nscd must be enabled through `services.nscd.enable` for SSSD to work."; + }; + + systemd.services.sssd = { + description = "System Security Services Daemon"; + wantedBy = [ "multi-user.target" ]; + before = [ "systemd-user-sessions.service" "nss-user-lookup.target" ]; + after = [ "network-online.target" "nscd.service" ]; + requires = [ "network-online.target" "nscd.service" ]; + wants = [ "nss-user-lookup.target" ]; + restartTriggers = [ + config.environment.etc."nscd.conf".source + config.environment.etc."sssd/sssd.conf".source + ]; + script = '' + export LDB_MODULES_PATH+="''${LDB_MODULES_PATH+:}${pkgs.ldb}/modules/ldb:${pkgs.sssd}/modules/ldb" + mkdir -p /var/lib/sss/{pubconf,db,mc,pipes,gpo_cache,secrets} /var/lib/sss/pipes/private /var/lib/sss/pubconf/krb5.include.d + ${pkgs.sssd}/bin/sssd -D + ''; + serviceConfig = { + Type = "forking"; + PIDFile = "/run/sssd.pid"; + }; + }; + + environment.etc."sssd/sssd.conf" = { + text = cfg.config; + mode = "0400"; + }; + + system.nssModules = optional cfg.enable pkgs.sssd; + services.nscd.config = builtins.readFile ./nscd-sssd.conf; + services.dbus.packages = [ pkgs.sssd ]; + }) + + (mkIf cfg.sshAuthorizedKeysIntegration { + # Ugly: sshd refuses to start if a store path is given because /nix/store is group-writable. + # So indirect by a symlink. + environment.etc."ssh/authorized_keys_command" = { + mode = "0755"; + text = '' + #!/bin/sh + exec ${pkgs.sssd}/bin/sss_ssh_authorizedkeys "$@" + ''; + }; + services.openssh.extraConfig = '' + AuthorizedKeysCommand /etc/ssh/authorized_keys_command + AuthorizedKeysCommandUser nobody + ''; + })]; +} diff --git a/nixos/modules/services/misc/svnserve.nix b/nixos/modules/services/misc/svnserve.nix index c74befac749..04a6cd7bfa9 100644 --- a/nixos/modules/services/misc/svnserve.nix +++ b/nixos/modules/services/misc/svnserve.nix @@ -35,7 +35,7 @@ in config = mkIf cfg.enable { systemd.services.svnserve = { - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; preStart = "mkdir -p ${cfg.svnBaseDir}"; script = "${pkgs.subversion.out}/bin/svnserve -r ${cfg.svnBaseDir} -d --foreground --pid-file=/var/run/svnserve.pid"; diff --git a/nixos/modules/services/misc/taskserver/default.nix b/nixos/modules/services/misc/taskserver/default.nix index 6d458feec34..ca82a733f6f 100644 --- a/nixos/modules/services/misc/taskserver/default.nix +++ b/nixos/modules/services/misc/taskserver/default.nix @@ -154,7 +154,7 @@ let certtool = "${pkgs.gnutls.bin}/bin/certtool"; - nixos-taskserver = pkgs.buildPythonPackage { + nixos-taskserver = pkgs.pythonPackages.buildPythonPackage { name = "nixos-taskserver"; namePrefix = ""; @@ -292,7 +292,7 @@ in { }; allowedClientIDs = mkOption { - type = with types; loeOf (either (enum ["all" "none"]) str); + type = with types; either str (listOf str); default = []; example = [ "[Tt]ask [2-9]+" ]; description = '' @@ -306,7 +306,7 @@ in { }; disallowedClientIDs = mkOption { - type = with types; loeOf (either (enum ["all" "none"]) str); + type = with types; either str (listOf str); default = []; example = [ "[Tt]ask [2-9]+" ]; description = '' diff --git a/nixos/modules/services/misc/zookeeper.nix b/nixos/modules/services/misc/zookeeper.nix index 4ce692b6f6a..b7bca8b56b2 100644 --- a/nixos/modules/services/misc/zookeeper.nix +++ b/nixos/modules/services/misc/zookeeper.nix @@ -113,7 +113,7 @@ in { systemd.services.zookeeper = { description = "Zookeeper Daemon"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; environment = { ZOOCFGDIR = configDir; }; serviceConfig = { ExecStart = '' diff --git a/nixos/modules/services/monitoring/bosun.nix b/nixos/modules/services/monitoring/bosun.nix index 9a1e790d3ab..496838a131b 100644 --- a/nixos/modules/services/monitoring/bosun.nix +++ b/nixos/modules/services/monitoring/bosun.nix @@ -107,7 +107,7 @@ in { }; extraConfig = mkOption { - type = types.string; + type = types.lines; default = ""; description = '' Extra configuration options for Bosun. You should describe your diff --git a/nixos/modules/services/monitoring/cadvisor.nix b/nixos/modules/services/monitoring/cadvisor.nix index a67df158be4..8ae8b12056c 100644 --- a/nixos/modules/services/monitoring/cadvisor.nix +++ b/nixos/modules/services/monitoring/cadvisor.nix @@ -90,6 +90,7 @@ in { ${optionalString cfg.storageDriverSecure "-storage_driver_secure"} ''} ''; + TimeoutStartSec=300; }; }; diff --git a/nixos/modules/services/monitoring/collectd.nix b/nixos/modules/services/monitoring/collectd.nix index 3c3d83c66ed..641da60e9ad 100644 --- a/nixos/modules/services/monitoring/collectd.nix +++ b/nixos/modules/services/monitoring/collectd.nix @@ -9,7 +9,7 @@ let BaseDir "${cfg.dataDir}" PIDFile "${cfg.pidFile}" AutoLoadPlugin ${if cfg.autoLoadPlugin then "true" else "false"} - Hostname ${config.networking.hostName} + Hostname "${config.networking.hostName}" LoadPlugin syslog <Plugin "syslog"> @@ -108,7 +108,8 @@ in { }; preStart = '' - mkdir -m 0700 -p ${cfg.dataDir} + mkdir -p ${cfg.dataDir} + chmod 755 ${cfg.dataDir} install -D /dev/null ${cfg.pidFile} if [ "$(id -u)" = 0 ]; then chown -R ${cfg.user} ${cfg.dataDir}; diff --git a/nixos/modules/services/monitoring/graphite.nix b/nixos/modules/services/monitoring/graphite.nix index 08fc3f04dbf..c5352e5887d 100644 --- a/nixos/modules/services/monitoring/graphite.nix +++ b/nixos/modules/services/monitoring/graphite.nix @@ -167,7 +167,7 @@ in { CACHE_TYPE: 'filesystem' CACHE_DIR: '/tmp/graphite-api-cache' ''; - type = types.str; + type = types.lines; }; }; @@ -387,7 +387,7 @@ in { systemd.services.carbonCache = let name = "carbon-cache"; in { description = "Graphite Data Storage Backend"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; environment = carbonEnv; serviceConfig = { ExecStart = "${pkgs.pythonPackages.twisted}/bin/twistd ${carbonOpts name}"; @@ -410,7 +410,7 @@ in { enable = cfg.carbon.enableAggregator; description = "Carbon Data Aggregator"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; environment = carbonEnv; serviceConfig = { ExecStart = "${pkgs.pythonPackages.twisted}/bin/twistd ${carbonOpts name}"; @@ -426,7 +426,7 @@ in { systemd.services.carbonRelay = let name = "carbon-relay"; in { description = "Carbon Data Relay"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; environment = carbonEnv; serviceConfig = { ExecStart = "${pkgs.pythonPackages.twisted}/bin/twistd ${carbonOpts name}"; @@ -448,7 +448,7 @@ in { systemd.services.graphiteWeb = { description = "Graphite Web Interface"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; path = [ pkgs.perl ]; environment = { PYTHONPATH = let @@ -501,7 +501,7 @@ in { systemd.services.graphiteApi = { description = "Graphite Api Interface"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; environment = { PYTHONPATH = let aenv = pkgs.python.buildEnv.override { @@ -538,7 +538,7 @@ in { systemd.services.seyren = { description = "Graphite Alerting Dashboard"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" "mongodb.service" ]; + after = [ "network.target" "mongodb.service" ]; environment = seyrenConfig; serviceConfig = { ExecStart = "${pkgs.seyren}/bin/seyren -httpPort ${toString cfg.seyren.port}"; @@ -561,7 +561,7 @@ in { systemd.services.graphitePager = { description = "Graphite Pager Alerting Daemon"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" "redis.service" ]; + after = [ "network.target" "redis.service" ]; environment = { REDIS_URL = cfg.pager.redisUrl; GRAPHITE_URL = cfg.pager.graphiteUrl; @@ -585,7 +585,7 @@ in { serviceConfig = { ExecStart = '' ${pkgs.pythonPackages.graphite_beacon}/bin/graphite-beacon \ - --config ${pkgs.writeText "graphite-beacon.json" (builtins.toJSON cfg.beacon.config)} + --config=${pkgs.writeText "graphite-beacon.json" (builtins.toJSON cfg.beacon.config)} ''; User = "graphite"; Group = "graphite"; diff --git a/nixos/modules/services/monitoring/monit.nix b/nixos/modules/services/monitoring/monit.nix index 704693969a3..e07ffd2e8b5 100644 --- a/nixos/modules/services/monitoring/monit.nix +++ b/nixos/modules/services/monitoring/monit.nix @@ -36,11 +36,16 @@ in ]; systemd.services.monit = { - description = "Monit system watcher"; - after = [ "network-interfaces.target" ]; + description = "Pro-active monitoring utility for unix systems"; + after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - script = "${pkgs.monit}/bin/monit -I -c /etc/monit.conf"; - serviceConfig.Restart = "always"; + serviceConfig = { + ExecStart = "${pkgs.monit}/bin/monit -I -c /etc/monit.conf"; + ExecStop = "${pkgs.monit}/bin/monit -c /etc/monit.conf quit"; + ExecReload = "${pkgs.monit}/bin/monit -c /etc/monit.conf reload"; + KillMode = "process"; + Restart = "always"; + }; }; }; } diff --git a/nixos/modules/services/monitoring/munin.nix b/nixos/modules/services/monitoring/munin.nix index 08ba161d38b..a80565fa280 100644 --- a/nixos/modules/services/monitoring/munin.nix +++ b/nixos/modules/services/monitoring/munin.nix @@ -100,6 +100,7 @@ in extraConfig = mkOption { default = ""; + type = types.lines; description = '' <filename>munin-node.conf</filename> extra configuration. See <link xlink:href='http://munin-monitoring.org/wiki/munin-node.conf' /> diff --git a/nixos/modules/services/monitoring/nagios.nix b/nixos/modules/services/monitoring/nagios.nix index f2f7710de9e..4914c5db97d 100644 --- a/nixos/modules/services/monitoring/nagios.nix +++ b/nixos/modules/services/monitoring/nagios.nix @@ -163,7 +163,7 @@ in description = "Nagios monitoring daemon"; path = [ pkgs.nagios ]; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; serviceConfig = { User = "nagios"; diff --git a/nixos/modules/services/monitoring/prometheus/alertmanager.nix b/nixos/modules/services/monitoring/prometheus/alertmanager.nix new file mode 100644 index 00000000000..da2cd02eaa3 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/alertmanager.nix @@ -0,0 +1,116 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.prometheus.alertmanager; + mkConfigFile = pkgs.writeText "alertmanager.yml" (builtins.toJSON cfg.configuration); +in { + options = { + services.prometheus.alertmanager = { + enable = mkEnableOption "Prometheus Alertmanager"; + + user = mkOption { + type = types.str; + default = "nobody"; + description = '' + User name under which Alertmanager shall be run. + ''; + }; + + group = mkOption { + type = types.str; + default = "nogroup"; + description = '' + Group under which Alertmanager shall be run. + ''; + }; + + configuration = mkOption { + type = types.attrs; + default = {}; + description = '' + Alertmanager configuration as nix attribute set. + ''; + }; + + logFormat = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + If set use a syslog logger or JSON logging. + ''; + }; + + logLevel = mkOption { + type = types.enum ["debug" "info" "warn" "error" "fatal"]; + default = "warn"; + description = '' + Only log messages with the given severity or above. + ''; + }; + + webExternalUrl = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + The URL under which Alertmanager is externally reachable (for example, if Alertmanager is served via a reverse proxy). + Used for generating relative and absolute links back to Alertmanager itself. + If the URL has a path portion, it will be used to prefix all HTTP endoints served by Alertmanager. + If omitted, relevant URL components will be derived automatically. + ''; + }; + + listenAddress = mkOption { + type = types.str; + default = ""; + description = '' + Address to listen on for the web interface and API. + ''; + }; + + port = mkOption { + type = types.int; + default = 9093; + description = '' + Port to listen on for the web interface and API. + ''; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Open port in firewall for incoming connections. + ''; + }; + }; + }; + + + config = mkIf cfg.enable { + networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port; + + systemd.services.alertmanager = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + script = '' + ${pkgs.prometheus-alertmanager.bin}/bin/alertmanager \ + -config.file ${mkConfigFile} \ + -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + -log.level ${cfg.logLevel} \ + ${optionalString (cfg.webExternalUrl != null) ''-web.external-url ${cfg.webExternalUrl} \''} + ${optionalString (cfg.logFormat != null) "-log.format ${cfg.logFormat}"} + ''; + + serviceConfig = { + User = cfg.user; + Group = cfg.group; + Restart = "always"; + PrivateTmp = true; + WorkingDirectory = "/tmp"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + }; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix b/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix new file mode 100644 index 00000000000..7a343299c31 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix @@ -0,0 +1,67 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.prometheus.blackboxExporter; +in { + options = { + services.prometheus.blackboxExporter = { + enable = mkEnableOption "prometheus blackbox exporter"; + + configFile = mkOption { + type = types.path; + description = '' + Path to configuration file. + ''; + }; + + port = mkOption { + type = types.int; + default = 9115; + description = '' + Port to listen on. + ''; + }; + + extraFlags = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Extra commandline options when launching the blackbox exporter. + ''; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Open port in firewall for incoming connections. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port; + + systemd.services.prometheus-blackbox-exporter = { + description = "Prometheus exporter for blackbox probes"; + unitConfig.Documentation = "https://github.com/prometheus/blackbox_exporter"; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + User = "nobody"; + Restart = "always"; + PrivateTmp = true; + WorkingDirectory = /tmp; + ExecStart = '' + ${pkgs.prometheus-blackbox-exporter}/bin/blackbox_exporter \ + -web.listen-address :${toString cfg.port} \ + -config.file ${cfg.configFile} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + }; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/default.nix b/nixos/modules/services/monitoring/prometheus/default.nix new file mode 100644 index 00000000000..a07445ce167 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/default.nix @@ -0,0 +1,465 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.prometheus; + promUser = "prometheus"; + promGroup = "prometheus"; + + # Get a submodule without any embedded metadata: + _filter = x: filterAttrs (k: v: k != "_module") x; + + # Pretty-print JSON to a file + writePrettyJSON = name: x: + pkgs.runCommand name { } '' + echo '${builtins.toJSON x}' | ${pkgs.jq}/bin/jq . > $out + ''; + + # This becomes the main config file + promConfig = { + global = cfg.globalConfig; + rule_files = cfg.ruleFiles ++ [ + (pkgs.writeText "prometheus.rules" (concatStringsSep "\n" cfg.rules)) + ]; + scrape_configs = cfg.scrapeConfigs; + }; + + generatedPrometheusYml = writePrettyJSON "prometheus.yml" promConfig; + + prometheusYml = + if cfg.configText != null then + pkgs.writeText "prometheus.yml" cfg.configText + else generatedPrometheusYml; + + cmdlineArgs = cfg.extraFlags ++ [ + "-storage.local.path=${cfg.dataDir}/metrics" + "-config.file=${prometheusYml}" + "-web.listen-address=${cfg.listenAddress}" + "-alertmanager.notification-queue-capacity=${toString cfg.alertmanagerNotificationQueueCapacity}" + "-alertmanager.timeout=${toString cfg.alertmanagerTimeout}s" + (optionalString (cfg.alertmanagerURL != []) "-alertmanager.url=${concatStringsSep "," cfg.alertmanagerURL}") + ]; + + promTypes.globalConfig = types.submodule { + options = { + scrape_interval = mkOption { + type = types.str; + default = "1m"; + description = '' + How frequently to scrape targets by default. + ''; + }; + + scrape_timeout = mkOption { + type = types.str; + default = "10s"; + description = '' + How long until a scrape request times out. + ''; + }; + + evaluation_interval = mkOption { + type = types.str; + default = "1m"; + description = '' + How frequently to evaluate rules by default. + ''; + }; + + labels = mkOption { + type = types.attrsOf types.str; + default = {}; + description = '' + The labels to add to any timeseries that this Prometheus instance + scrapes. + ''; + }; + }; + }; + + promTypes.scrape_config = types.submodule { + options = { + job_name = mkOption { + type = types.str; + description = '' + The job name assigned to scraped metrics by default. + ''; + }; + scrape_interval = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + How frequently to scrape targets from this job. Defaults to the + globally configured default. + ''; + }; + scrape_timeout = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Per-target timeout when scraping this job. Defaults to the + globally configured default. + ''; + }; + metrics_path = mkOption { + type = types.str; + default = "/metrics"; + description = '' + The HTTP resource path on which to fetch metrics from targets. + ''; + }; + scheme = mkOption { + type = types.enum ["http" "https"]; + default = "http"; + description = '' + The URL scheme with which to fetch metrics from targets. + ''; + }; + basic_auth = mkOption { + type = types.nullOr (types.submodule { + options = { + username = mkOption { + type = types.str; + description = '' + HTTP username + ''; + }; + password = mkOption { + type = types.str; + description = '' + HTTP password + ''; + }; + }; + }); + default = null; + description = '' + Optional http login credentials for metrics scraping. + ''; + }; + dns_sd_configs = mkOption { + type = types.listOf promTypes.dns_sd_config; + default = []; + apply = x: map _filter x; + description = '' + List of DNS service discovery configurations. + ''; + }; + consul_sd_configs = mkOption { + type = types.listOf promTypes.consul_sd_config; + default = []; + apply = x: map _filter x; + description = '' + List of Consul service discovery configurations. + ''; + }; + file_sd_configs = mkOption { + type = types.listOf promTypes.file_sd_config; + default = []; + apply = x: map _filter x; + description = '' + List of file service discovery configurations. + ''; + }; + static_configs = mkOption { + type = types.listOf promTypes.static_config; + default = []; + apply = x: map _filter x; + description = '' + List of labeled target groups for this job. + ''; + }; + relabel_configs = mkOption { + type = types.listOf promTypes.relabel_config; + default = []; + apply = x: map _filter x; + description = '' + List of relabel configurations. + ''; + }; + }; + }; + + promTypes.static_config = types.submodule { + options = { + targets = mkOption { + type = types.listOf types.str; + description = '' + The targets specified by the target group. + ''; + }; + labels = mkOption { + type = types.attrsOf types.str; + default = {}; + description = '' + Labels assigned to all metrics scraped from the targets. + ''; + }; + }; + }; + + promTypes.dns_sd_config = types.submodule { + options = { + names = mkOption { + type = types.listOf types.str; + description = '' + A list of DNS SRV record names to be queried. + ''; + }; + refresh_interval = mkOption { + type = types.str; + default = "30s"; + description = '' + The time after which the provided names are refreshed. + ''; + }; + }; + }; + + promTypes.consul_sd_config = types.submodule { + options = { + server = mkOption { + type = types.str; + description = "Consul server to query."; + }; + token = mkOption { + type = types.nullOr types.str; + description = "Consul token"; + }; + datacenter = mkOption { + type = types.nullOr types.str; + description = "Consul datacenter"; + }; + scheme = mkOption { + type = types.nullOr types.str; + description = "Consul scheme"; + }; + username = mkOption { + type = types.nullOr types.str; + description = "Consul username"; + }; + password = mkOption { + type = types.nullOr types.str; + description = "Consul password"; + }; + + services = mkOption { + type = types.listOf types.str; + description = '' + A list of services for which targets are retrieved. + ''; + }; + tag_separator = mkOption { + type = types.str; + default = ","; + description = '' + The string by which Consul tags are joined into the tag label. + ''; + }; + }; + }; + + promTypes.file_sd_config = types.submodule { + options = { + files = mkOption { + type = types.listOf types.str; + description = '' + Patterns for files from which target groups are extracted. Refer + to the Prometheus documentation for permitted filename patterns + and formats. + + ''; + }; + refresh_interval = mkOption { + type = types.str; + default = "30s"; + description = '' + Refresh interval to re-read the files. + ''; + }; + }; + }; + + promTypes.relabel_config = types.submodule { + options = { + source_labels = mkOption { + type = types.listOf types.str; + description = '' + The source labels select values from existing labels. Their content + is concatenated using the configured separator and matched against + the configured regular expression. + ''; + }; + separator = mkOption { + type = types.str; + default = ";"; + description = '' + Separator placed between concatenated source label values. + ''; + }; + target_label = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Label to which the resulting value is written in a replace action. + It is mandatory for replace actions. + ''; + }; + regex = mkOption { + type = types.str; + default = "(.*)"; + description = '' + Regular expression against which the extracted value is matched. + ''; + }; + replacement = mkOption { + type = types.str; + default = "$1"; + description = '' + Replacement value against which a regex replace is performed if the + regular expression matches. + ''; + }; + action = mkOption { + type = types.enum ["replace" "keep" "drop"]; + default = "replace"; + description = '' + Action to perform based on regex matching. + ''; + }; + }; + }; + +in { + options = { + services.prometheus = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enable the Prometheus monitoring daemon. + ''; + }; + + listenAddress = mkOption { + type = types.str; + default = "0.0.0.0:9090"; + description = '' + Address to listen on for the web interface, API, and telemetry. + ''; + }; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/prometheus"; + description = '' + Directory to store Prometheus metrics data. + ''; + }; + + extraFlags = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Extra commandline options when launching Prometheus. + ''; + }; + + configText = mkOption { + type = types.nullOr types.lines; + default = null; + description = '' + If non-null, this option defines the text that is written to + prometheus.yml. If null, the contents of prometheus.yml is generated + from the structured config options. + ''; + }; + + globalConfig = mkOption { + type = promTypes.globalConfig; + default = {}; + apply = _filter; + description = '' + Parameters that are valid in all configuration contexts. They + also serve as defaults for other configuration sections + ''; + }; + + rules = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Alerting and/or Recording rules to evaluate at runtime. + ''; + }; + + ruleFiles = mkOption { + type = types.listOf types.path; + default = []; + description = '' + Any additional rules files to include in this configuration. + ''; + }; + + scrapeConfigs = mkOption { + type = types.listOf promTypes.scrape_config; + default = []; + apply = x: map _filter x; + description = '' + A list of scrape configurations. + ''; + }; + + alertmanagerURL = mkOption { + type = types.listOf types.str; + default = []; + description = '' + List of Alertmanager URLs to send notifications to. + ''; + }; + + alertmanagerNotificationQueueCapacity = mkOption { + type = types.int; + default = 10000; + description = '' + The capacity of the queue for pending alert manager notifications. + ''; + }; + + alertmanagerTimeout = mkOption { + type = types.int; + default = 10; + description = '' + Alert manager HTTP API timeout (in seconds). + ''; + }; + }; + }; + + config = mkIf cfg.enable { + users.extraGroups.${promGroup}.gid = config.ids.gids.prometheus; + users.extraUsers.${promUser} = { + description = "Prometheus daemon user"; + uid = config.ids.uids.prometheus; + group = promGroup; + home = cfg.dataDir; + createHome = true; + }; + systemd.services.prometheus = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + script = '' + #!/bin/sh + exec ${pkgs.prometheus}/bin/prometheus \ + ${concatStringsSep " \\\n " cmdlineArgs} + ''; + serviceConfig = { + User = promUser; + Restart = "always"; + WorkingDirectory = cfg.dataDir; + }; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/json-exporter.nix b/nixos/modules/services/monitoring/prometheus/json-exporter.nix new file mode 100644 index 00000000000..6bc56df9834 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/json-exporter.nix @@ -0,0 +1,74 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.prometheus.jsonExporter; +in { + options = { + services.prometheus.jsonExporter = { + enable = mkEnableOption "prometheus JSON exporter"; + + url = mkOption { + type = types.str; + description = '' + URL to scrape JSON from. + ''; + }; + + configFile = mkOption { + type = types.path; + description = '' + Path to configuration file. + ''; + }; + + port = mkOption { + type = types.int; + default = 7979; + description = '' + Port to listen on. + ''; + }; + + extraFlags = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Extra commandline options when launching the JSON exporter. + ''; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Open port in firewall for incoming connections. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port; + + systemd.services.prometheus-json-exporter = { + description = "Prometheus exporter for JSON over HTTP"; + unitConfig.Documentation = "https://github.com/kawamuray/prometheus-json-exporter"; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + User = "nobody"; + Restart = "always"; + PrivateTmp = true; + WorkingDirectory = /tmp; + ExecStart = '' + ${pkgs.prometheus-json-exporter}/bin/prometheus-json-exporter \ + --port ${toString cfg.port} \ + ${cfg.url} ${cfg.configFile} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + }; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/nginx-exporter.nix b/nixos/modules/services/monitoring/prometheus/nginx-exporter.nix new file mode 100644 index 00000000000..1ccafee3b18 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/nginx-exporter.nix @@ -0,0 +1,78 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.prometheus.nginxExporter; +in { + options = { + services.prometheus.nginxExporter = { + enable = mkEnableOption "prometheus nginx exporter"; + + port = mkOption { + type = types.int; + default = 9113; + description = '' + Port to listen on. + ''; + }; + + listenAddress = mkOption { + type = types.string; + default = "0.0.0.0"; + description = '' + Address to listen on. + ''; + }; + + scrapeUri = mkOption { + type = types.string; + default = "http://localhost/nginx_status"; + description = '' + Address to access the nginx status page. + Can be enabled with services.nginx.statusPage = true. + ''; + }; + + extraFlags = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Extra commandline options when launching the nginx exporter. + ''; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Open port in firewall for incoming connections. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port; + + systemd.services.prometheus-nginx-exporter = { + after = [ "network.target" "nginx.service" ]; + description = "Prometheus exporter for nginx metrics"; + unitConfig.Documentation = "https://github.com/discordianfish/nginx_exporter"; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + User = "nobody"; + Restart = "always"; + PrivateTmp = true; + WorkingDirectory = /tmp; + ExecStart = '' + ${pkgs.prometheus-nginx-exporter}/bin/nginx_exporter \ + -nginx.scrape_uri '${cfg.scrapeUri}' \ + -telemetry.address ${cfg.listenAddress}:${toString cfg.port} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + }; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/node-exporter.nix b/nixos/modules/services/monitoring/prometheus/node-exporter.nix new file mode 100644 index 00000000000..0cf0b85afb5 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/node-exporter.nix @@ -0,0 +1,81 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.prometheus.nodeExporter; + cmdlineArgs = cfg.extraFlags ++ [ + "-web.listen-address=${cfg.listenAddress}" + ]; +in { + options = { + services.prometheus.nodeExporter = { + enable = mkEnableOption "prometheus node exporter"; + + port = mkOption { + type = types.int; + default = 9100; + description = '' + Port to listen on. + ''; + }; + + listenAddress = mkOption { + type = types.string; + default = "0.0.0.0"; + description = '' + Address to listen on. + ''; + }; + + enabledCollectors = mkOption { + type = types.listOf types.string; + default = []; + example = ''[ "systemd" ]''; + description = '' + Collectors to enable, additionally to the defaults. + ''; + }; + + extraFlags = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Extra commandline options when launching the node exporter. + ''; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Open port in firewall for incoming connections. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port; + + systemd.services.prometheus-node-exporter = { + description = "Prometheus exporter for machine metrics"; + unitConfig.Documentation = "https://github.com/prometheus/node_exporter"; + wantedBy = [ "multi-user.target" ]; + script = '' + exec ${pkgs.prometheus-node-exporter}/bin/node_exporter \ + ${optionalString (cfg.enabledCollectors != []) + ''-collectors.enabled ${concatStringsSep "," cfg.enabledCollectors}''} \ + -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + serviceConfig = { + User = "nobody"; + Restart = "always"; + PrivateTmp = true; + WorkingDirectory = /tmp; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + }; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/snmp-exporter.nix b/nixos/modules/services/monitoring/prometheus/snmp-exporter.nix new file mode 100644 index 00000000000..fe33f8c1f04 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/snmp-exporter.nix @@ -0,0 +1,127 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.prometheus.snmpExporter; + mkConfigFile = pkgs.writeText "snmp.yml" (if cfg.configurationPath == null then builtins.toJSON cfg.configuration else builtins.readFile cfg.configurationPath); +in { + options = { + services.prometheus.snmpExporter = { + enable = mkEnableOption "Prometheus snmp exporter"; + + user = mkOption { + type = types.str; + default = "nobody"; + description = '' + User name under which snmp exporter shall be run. + ''; + }; + + group = mkOption { + type = types.str; + default = "nogroup"; + description = '' + Group under which snmp exporter shall be run. + ''; + }; + + port = mkOption { + type = types.int; + default = 9116; + description = '' + Port to listen on. + ''; + }; + + listenAddress = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Address to listen on for web interface and telemetry. + ''; + }; + + configurationPath = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Path to a snmp exporter configuration file. Mutually exclusive with 'configuration' option. + ''; + example = "./snmp.yml"; + }; + + configuration = mkOption { + type = types.nullOr types.attrs; + default = {}; + description = '' + Snmp exporter configuration as nix attribute set. Mutually exclusive with 'configurationPath' option. + ''; + example = '' + { + "default" = { + "version" = 2; + "auth" = { + "community" = "public"; + }; + }; + }; + ''; + }; + + logFormat = mkOption { + type = types.str; + default = "logger:stderr"; + description = '' + Set the log target and format. + ''; + }; + + logLevel = mkOption { + type = types.enum ["debug" "info" "warn" "error" "fatal"]; + default = "info"; + description = '' + Only log messages with the given severity or above. + ''; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Open port in firewall for incoming connections. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port; + + assertions = singleton + { + assertion = (cfg.configurationPath == null) != (cfg.configuration == null); + message = "Please ensure you have either 'configuration' or 'configurationPath' set!"; + }; + + systemd.services.prometheus-snmp-exporter = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + script = '' + ${pkgs.prometheus-snmp-exporter.bin}/bin/snmp_exporter \ + -config.file ${mkConfigFile} \ + -log.format ${cfg.logFormat} \ + -log.level ${cfg.logLevel} \ + -web.listen-address ${optionalString (cfg.listenAddress != null) cfg.listenAddress}:${toString cfg.port} + ''; + + serviceConfig = { + User = cfg.user; + Group = cfg.group; + Restart = "always"; + PrivateTmp = true; + WorkingDirectory = "/tmp"; + }; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/varnish-exporter.nix b/nixos/modules/services/monitoring/prometheus/varnish-exporter.nix new file mode 100644 index 00000000000..143ebb62aea --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/varnish-exporter.nix @@ -0,0 +1,61 @@ +{ config, pkgs, lib, ... }: + +# Shamelessly cribbed from nginx-exporter.nix. ~ C. +with lib; + +let + cfg = config.services.prometheus.varnishExporter; +in { + options = { + services.prometheus.varnishExporter = { + enable = mkEnableOption "prometheus Varnish exporter"; + + port = mkOption { + type = types.int; + default = 9131; + description = '' + Port to listen on. + ''; + }; + + extraFlags = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Extra commandline options when launching the Varnish exporter. + ''; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Open port in firewall for incoming connections. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port; + + systemd.services.prometheus-varnish-exporter = { + description = "Prometheus exporter for Varnish metrics"; + unitConfig.Documentation = "https://github.com/jonnenauha/prometheus_varnish_exporter"; + wantedBy = [ "multi-user.target" ]; + path = [ pkgs.varnish ]; + script = '' + exec ${pkgs.prometheus-varnish-exporter}/bin/prometheus_varnish_exporter \ + -web.listen-address :${toString cfg.port} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + serviceConfig = { + User = "nobody"; + Restart = "always"; + PrivateTmp = true; + WorkingDirectory = /tmp; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + }; + }; + }; +} diff --git a/nixos/modules/services/monitoring/riemann-tools.nix b/nixos/modules/services/monitoring/riemann-tools.nix index ce277f09464..de858813a76 100644 --- a/nixos/modules/services/monitoring/riemann-tools.nix +++ b/nixos/modules/services/monitoring/riemann-tools.nix @@ -50,6 +50,7 @@ in { systemd.services.riemann-health = { wantedBy = [ "multi-user.target" ]; + path = [ procps ]; serviceConfig = { User = "riemanntools"; ExecStart = "${healthLauncher}/bin/riemann-health"; diff --git a/nixos/modules/services/monitoring/smartd.nix b/nixos/modules/services/monitoring/smartd.nix index b67519b3424..99fd5c4d367 100644 --- a/nixos/modules/services/monitoring/smartd.nix +++ b/nixos/modules/services/monitoring/smartd.nix @@ -197,8 +197,7 @@ in devices = mkOption { default = []; example = [ { device = "/dev/sda"; } { device = "/dev/sdb"; options = "-d sat"; } ]; - type = types.listOf types.optionSet; - options = [ smartdOpts ]; + type = with types; listOf (submodule smartdOpts); description = "List of devices to monitor."; }; diff --git a/nixos/modules/services/monitoring/telegraf.nix b/nixos/modules/services/monitoring/telegraf.nix new file mode 100644 index 00000000000..49dc9d8143e --- /dev/null +++ b/nixos/modules/services/monitoring/telegraf.nix @@ -0,0 +1,71 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.telegraf; + + configFile = pkgs.runCommand "config.toml" { + buildInputs = [ pkgs.remarshal ]; + } '' + remarshal -if json -of toml \ + < ${pkgs.writeText "config.json" (builtins.toJSON cfg.extraConfig)} \ + > $out + ''; +in { + ###### interface + options = { + services.telegraf = { + enable = mkEnableOption "telegraf server"; + + package = mkOption { + default = pkgs.telegraf; + defaultText = "pkgs.telegraf"; + description = "Which telegraf derivation to use"; + type = types.package; + }; + + extraConfig = mkOption { + default = {}; + description = "Extra configuration options for telegraf"; + type = types.attrs; + example = { + outputs = { + influxdb = { + urls = ["http://localhost:8086"]; + database = "telegraf"; + }; + }; + inputs = { + statsd = { + service_address = ":8125"; + delete_timings = true; + }; + }; + }; + }; + }; + }; + + + ###### implementation + config = mkIf config.services.telegraf.enable { + systemd.services.telegraf = { + description = "Telegraf Agent"; + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" ]; + serviceConfig = { + ExecStart=''${cfg.package}/bin/telegraf -config "${configFile}"''; + ExecReload="${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + User = "telegraf"; + Restart = "on-failure"; + }; + }; + + users.extraUsers = [{ + name = "telegraf"; + uid = config.ids.uids.telegraf; + description = "telegraf daemon user"; + }]; + }; +} diff --git a/nixos/modules/services/monitoring/ups.nix b/nixos/modules/services/monitoring/ups.nix index 5f80d547dbc..c4c4ed227b3 100644 --- a/nixos/modules/services/monitoring/ups.nix +++ b/nixos/modules/services/monitoring/ups.nix @@ -169,8 +169,7 @@ in monitoring directly. These are usually attached to serial ports, but USB devices are also supported. ''; - type = types.attrsOf types.optionSet; - options = [ upsOptions ]; + type = with types; attrsOf (submodule upsOptions); }; }; @@ -182,7 +181,8 @@ in systemd.services.upsmon = { description = "Uninterruptible Power Supplies (Monitor)"; - wantedBy = [ "ip-up.target" ]; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; serviceConfig.Type = "forking"; script = "${pkgs.nut}/sbin/upsmon"; environment.NUT_CONFPATH = "/etc/nut/"; @@ -191,8 +191,8 @@ in systemd.services.upsd = { description = "Uninterruptible Power Supplies (Daemon)"; + after = [ "network.target" "upsmon.service" ]; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" "upsmon.service" ]; serviceConfig.Type = "forking"; # TODO: replace 'root' by another username. script = "${pkgs.nut}/sbin/upsd -u root"; @@ -202,8 +202,8 @@ in systemd.services.upsdrv = { description = "Uninterruptible Power Supplies (Register all UPS)"; - wantedBy = [ "multi-user.target" ]; after = [ "upsd.service" ]; + wantedBy = [ "multi-user.target" ]; # TODO: replace 'root' by another username. script = ''${pkgs.nut}/bin/upsdrvctl -u root start''; serviceConfig = { diff --git a/nixos/modules/services/monitoring/zabbix-agent.nix b/nixos/modules/services/monitoring/zabbix-agent.nix index a943075be0c..88a63b4bf16 100644 --- a/nixos/modules/services/monitoring/zabbix-agent.nix +++ b/nixos/modules/services/monitoring/zabbix-agent.nix @@ -53,6 +53,7 @@ in extraConfig = mkOption { default = ""; + type = types.lines; description = '' Configuration that is injected verbatim into the configuration file. ''; diff --git a/nixos/modules/services/network-filesystems/cachefilesd.nix b/nixos/modules/services/network-filesystems/cachefilesd.nix new file mode 100644 index 00000000000..61981340840 --- /dev/null +++ b/nixos/modules/services/network-filesystems/cachefilesd.nix @@ -0,0 +1,59 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + + cfg = config.services.cachefilesd; + + cfgFile = pkgs.writeText "cachefilesd.conf" '' + dir ${cfg.cacheDir} + ${cfg.extraConfig} + ''; + +in + +{ + options = { + services.cachefilesd = { + + enable = mkOption { + type = types.bool; + default = false; + description = "Whether to enable cachefilesd network filesystems caching daemon."; + }; + + cacheDir = mkOption { + type = types.str; + default = "/var/cache/fscache"; + description = "Directory to contain filesystem cache."; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + example = "brun 10%"; + description = "Additional configuration file entries. See cachefilesd.conf(5) for more information."; + }; + + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + + systemd.services.cachefilesd = { + description = "Local network file caching management daemon"; + wantedBy = [ "multi-user.target" ]; + path = [ pkgs.kmod pkgs.cachefilesd ]; + script = '' + modprobe -qab cachefiles + mkdir -p ${cfg.cacheDir} + chmod 700 ${cfg.cacheDir} + exec cachefilesd -n -f ${cfgFile} + ''; + }; + + }; +} diff --git a/nixos/modules/services/network-filesystems/drbd.nix b/nixos/modules/services/network-filesystems/drbd.nix index 9896a93b189..57b1fbb597c 100644 --- a/nixos/modules/services/network-filesystems/drbd.nix +++ b/nixos/modules/services/network-filesystems/drbd.nix @@ -53,9 +53,9 @@ let cfg = config.services.drbd; in }; systemd.services.drbd = { - after = [ "systemd-udev.settle.service" ]; + after = [ "systemd-udev.settle.service" "network.target" ]; wants = [ "systemd-udev.settle.service" ]; - wantedBy = [ "ip-up.target" ]; + wantedBy = [ "multi-user.target" ]; script = '' ${pkgs.drbd}/sbin/drbdadm up all ''; diff --git a/nixos/modules/services/network-filesystems/ipfs.nix b/nixos/modules/services/network-filesystems/ipfs.nix new file mode 100644 index 00000000000..104b5b92620 --- /dev/null +++ b/nixos/modules/services/network-filesystems/ipfs.nix @@ -0,0 +1,125 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + inherit (pkgs) ipfs; + + cfg = config.services.ipfs; + + ipfsFlags = ''${if cfg.autoMigrate then "--migrate" else ""} ${if cfg.enableGC then "--enable-gc" else ""} ${toString cfg.extraFlags}''; + +in + +{ + + ###### interface + + options = { + + services.ipfs = { + + enable = mkEnableOption "Interplanetary File System"; + + user = mkOption { + type = types.str; + default = "ipfs"; + description = "User under which the IPFS daemon runs"; + }; + + group = mkOption { + type = types.str; + default = "ipfs"; + description = "Group under which the IPFS daemon runs"; + }; + + dataDir = mkOption { + type = types.str; + default = "/var/lib/ipfs"; + description = "The data dir for IPFS"; + }; + + autoMigrate = mkOption { + type = types.bool; + default = false; + description = '' + Whether IPFS should try to migrate the file system automatically. + ''; + }; + + gatewayAddress = mkOption { + type = types.str; + default = "/ip4/127.0.0.1/tcp/8080"; + description = "Where the IPFS Gateway can be reached"; + }; + + apiAddress = mkOption { + type = types.str; + default = "/ip4/127.0.0.1/tcp/5001"; + description = "Where IPFS exposes its API to"; + }; + + enableGC = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable automatic garbage collection. + ''; + }; + + extraFlags = mkOption { + type = types.listOf types.str; + description = "Extra flags passed to the IPFS daemon"; + default = []; + }; + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.ipfs ]; + + users.extraUsers = mkIf (cfg.user == "ipfs") { + ipfs = { + group = cfg.group; + home = cfg.dataDir; + createHome = false; + uid = config.ids.uids.ipfs; + description = "IPFS daemon user"; + }; + }; + + users.extraGroups = mkIf (cfg.group == "ipfs") { + ipfs = { + gid = config.ids.gids.ipfs; + }; + }; + + systemd.services.ipfs = { + description = "IPFS Daemon"; + + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" "local-fs.target" ]; + path = [ pkgs.ipfs pkgs.su pkgs.bash ]; + + preStart = + '' + install -m 0755 -o ${cfg.user} -g ${cfg.group} -d ${cfg.dataDir} + if [[ ! -d ${cfg.dataDir}/.ipfs ]]; then + cd ${cfg.dataDir} + ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c "${ipfs}/bin/ipfs init" + fi + ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c "${ipfs}/bin/ipfs config Addresses.API ${cfg.apiAddress}" + ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c "${ipfs}/bin/ipfs config Addresses.Gateway ${cfg.gatewayAddress}" + ''; + + serviceConfig = { + ExecStart = "${ipfs}/bin/ipfs daemon ${ipfsFlags}"; + User = cfg.user; + Group = cfg.group; + PermissionsStartOnly = true; + }; + }; + }; +} diff --git a/nixos/modules/services/network-filesystems/openafs-client/default.nix b/nixos/modules/services/network-filesystems/openafs-client/default.nix index 891f41c8dcd..6f51e287910 100644 --- a/nixos/modules/services/network-filesystems/openafs-client/default.nix +++ b/nixos/modules/services/network-filesystems/openafs-client/default.nix @@ -75,7 +75,7 @@ in systemd.services.afsd = { description = "AFS client"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; preStart = '' mkdir -p -m 0755 /afs diff --git a/nixos/modules/services/network-filesystems/samba.nix b/nixos/modules/services/network-filesystems/samba.nix index 91f1a333be7..884966363b8 100644 --- a/nixos/modules/services/network-filesystems/samba.nix +++ b/nixos/modules/services/network-filesystems/samba.nix @@ -56,6 +56,7 @@ let serviceConfig = { ExecStart = "${samba}/sbin/${appName} ${args}"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + Type = "notify"; }; restartTriggers = [ configFile ]; @@ -167,12 +168,12 @@ in type = types.attrsOf (types.attrsOf types.unspecified); example = { public = - { path = "/srv/public"; - "read only" = true; - browseable = "yes"; - "guest ok" = "yes"; - comment = "Public samba share."; - }; + { path = "/srv/public"; + "read only" = true; + browseable = "yes"; + "guest ok" = "yes"; + comment = "Public samba share."; + }; }; }; diff --git a/nixos/modules/services/network-filesystems/tahoe.nix b/nixos/modules/services/network-filesystems/tahoe.nix index d4b6c05e943..ab9eac3829f 100644 --- a/nixos/modules/services/network-filesystems/tahoe.nix +++ b/nixos/modules/services/network-filesystems/tahoe.nix @@ -8,148 +8,189 @@ in options.services.tahoe = { introducers = mkOption { default = {}; - type = types.loaOf types.optionSet; + type = with types; loaOf (submodule { + options = { + nickname = mkOption { + type = types.str; + description = '' + The nickname of this Tahoe introducer. + ''; + }; + tub.port = mkOption { + default = 3458; + type = types.int; + description = '' + The port on which the introducer will listen. + ''; + }; + tub.location = mkOption { + default = null; + type = types.nullOr types.str; + description = '' + The external location that the introducer should listen on. + + If specified, the port should be included. + ''; + }; + package = mkOption { + default = pkgs.tahoelafs; + defaultText = "pkgs.tahoelafs"; + type = types.package; + example = literalExample "pkgs.tahoelafs"; + description = '' + The package to use for the Tahoe LAFS daemon. + ''; + }; + }; + }); description = '' The Tahoe introducers. ''; - options = { - nickname = mkOption { - type = types.str; - description = '' - The nickname of this Tahoe introducer. - ''; - }; - tub.port = mkOption { - default = 3458; - type = types.int; - description = '' - The port on which the introducer will listen. - ''; - }; - tub.location = mkOption { - default = null; - type = types.nullOr types.str; - description = '' - The external location that the introducer should listen on. - - If specified, the port should be included. - ''; - }; - package = mkOption { - default = pkgs.tahoelafs; - defaultText = "pkgs.tahoelafs"; - type = types.package; - example = literalExample "pkgs.tahoelafs"; - description = '' - The package to use for the Tahoe LAFS daemon. - ''; - }; - }; }; nodes = mkOption { default = {}; - type = types.loaOf types.optionSet; - description = '' - The Tahoe nodes. - ''; - options = { - nickname = mkOption { - type = types.str; - description = '' - The nickname of this Tahoe node. - ''; - }; - tub.port = mkOption { - default = 3457; - type = types.int; - description = '' - The port on which the tub will listen. + type = with types; loaOf (submodule { + options = { + nickname = mkOption { + type = types.str; + description = '' + The nickname of this Tahoe node. + ''; + }; + tub.port = mkOption { + default = 3457; + type = types.int; + description = '' + The port on which the tub will listen. - This is the correct setting to tweak if you want Tahoe's storage - system to listen on a different port. - ''; - }; - tub.location = mkOption { - default = null; - type = types.nullOr types.str; - description = '' - The external location that the node should listen on. + This is the correct setting to tweak if you want Tahoe's storage + system to listen on a different port. + ''; + }; + tub.location = mkOption { + default = null; + type = types.nullOr types.str; + description = '' + The external location that the node should listen on. - This is the setting to tweak if there are multiple interfaces - and you want to alter which interface Tahoe is advertising. + This is the setting to tweak if there are multiple interfaces + and you want to alter which interface Tahoe is advertising. - If specified, the port should be included. - ''; - }; - web.port = mkOption { - default = 3456; - type = types.int; - description = '' - The port on which the Web server will listen. + If specified, the port should be included. + ''; + }; + web.port = mkOption { + default = 3456; + type = types.int; + description = '' + The port on which the Web server will listen. - This is the correct setting to tweak if you want Tahoe's WUI to - listen on a different port. - ''; - }; - client.introducer = mkOption { - default = null; - type = types.nullOr types.str; - description = '' - The furl for a Tahoe introducer node. + This is the correct setting to tweak if you want Tahoe's WUI to + listen on a different port. + ''; + }; + client.introducer = mkOption { + default = null; + type = types.nullOr types.str; + description = '' + The furl for a Tahoe introducer node. - Like all furls, keep this safe and don't share it. - ''; - }; - client.helper = mkOption { - default = null; - type = types.nullOr types.str; - description = '' - The furl for a Tahoe helper node. + Like all furls, keep this safe and don't share it. + ''; + }; + client.helper = mkOption { + default = null; + type = types.nullOr types.str; + description = '' + The furl for a Tahoe helper node. - Like all furls, keep this safe and don't share it. - ''; - }; - client.shares.needed = mkOption { - default = 3; - type = types.int; - description = '' - The number of shares required to reconstitute a file. - ''; - }; - client.shares.happy = mkOption { - default = 7; - type = types.int; - description = '' - The number of distinct storage nodes required to store - a file. - ''; - }; - client.shares.total = mkOption { - default = 10; - type = types.int; - description = '' - The number of shares required to store a file. - ''; - }; - storage.enable = mkEnableOption "storage service"; - storage.reservedSpace = mkOption { - default = "1G"; - type = types.str; - description = '' - The amount of filesystem space to not use for storage. - ''; - }; - helper.enable = mkEnableOption "helper service"; - package = mkOption { - default = pkgs.tahoelafs; - defaultText = "pkgs.tahoelafs"; - type = types.package; - example = literalExample "pkgs.tahoelafs"; - description = '' - The package to use for the Tahoe LAFS daemon. - ''; + Like all furls, keep this safe and don't share it. + ''; + }; + client.shares.needed = mkOption { + default = 3; + type = types.int; + description = '' + The number of shares required to reconstitute a file. + ''; + }; + client.shares.happy = mkOption { + default = 7; + type = types.int; + description = '' + The number of distinct storage nodes required to store + a file. + ''; + }; + client.shares.total = mkOption { + default = 10; + type = types.int; + description = '' + The number of shares required to store a file. + ''; + }; + storage.enable = mkEnableOption "storage service"; + storage.reservedSpace = mkOption { + default = "1G"; + type = types.str; + description = '' + The amount of filesystem space to not use for storage. + ''; + }; + helper.enable = mkEnableOption "helper service"; + sftpd.enable = mkEnableOption "SFTP service"; + sftpd.port = mkOption { + default = null; + type = types.nullOr types.int; + description = '' + The port on which the SFTP server will listen. + + This is the correct setting to tweak if you want Tahoe's SFTP + daemon to listen on a different port. + ''; + }; + sftpd.hostPublicKeyFile = mkOption { + default = null; + type = types.nullOr types.path; + description = '' + Path to the SSH host public key. + ''; + }; + sftpd.hostPrivateKeyFile = mkOption { + default = null; + type = types.nullOr types.path; + description = '' + Path to the SSH host private key. + ''; + }; + sftpd.accounts.file = mkOption { + default = null; + type = types.nullOr types.path; + description = '' + Path to the accounts file. + ''; + }; + sftpd.accounts.url = mkOption { + default = null; + type = types.nullOr types.str; + description = '' + URL of the accounts server. + ''; + }; + package = mkOption { + default = pkgs.tahoelafs; + defaultText = "pkgs.tahoelafs"; + type = types.package; + example = literalExample "pkgs.tahoelafs"; + description = '' + The package to use for the Tahoe LAFS daemon. + ''; + }; }; - }; + }); + description = '' + The Tahoe nodes. + ''; }; }; config = mkMerge [ @@ -192,6 +233,12 @@ in serviceConfig = { Type = "simple"; PIDFile = pidfile; + # Believe it or not, Tahoe is very brittle about the order of + # arguments to $(tahoe start). The node directory must come first, + # and arguments which alter Twisted's behavior come afterwards. + ExecStart = '' + ${settings.package}/bin/tahoe start ${nodedir} -n -l- --pidfile=${pidfile} + ''; }; preStart = '' if [ \! -d ${nodedir} ]; then @@ -207,12 +254,6 @@ in # ln -s /etc/tahoe-lafs/introducer-${node}.cfg ${nodedir}/tahoe.cfg cp /etc/tahoe-lafs/introducer-${node}.cfg ${nodedir}/tahoe.cfg ''; - # Believe it or not, Tahoe is very brittle about the order of - # arguments to $(tahoe start). The node directory must come first, - # and arguments which alter Twisted's behavior come afterwards. - script = '' - tahoe start ${nodedir} -n -l- --pidfile=${pidfile} - ''; }); users.extraUsers = flip mapAttrs' cfg.introducers (node: _: nameValuePair "tahoe.introducer-${node}" { @@ -254,6 +295,19 @@ in [helper] enabled = ${if settings.helper.enable then "true" else "false"} + + [sftpd] + enabled = ${if settings.sftpd.enable then "true" else "false"} + ${optionalString (settings.sftpd.port != null) + "port = ${toString settings.sftpd.port}"} + ${optionalString (settings.sftpd.hostPublicKeyFile != null) + "host_pubkey_file = ${settings.sftpd.hostPublicKeyFile}"} + ${optionalString (settings.sftpd.hostPrivateKeyFile != null) + "host_privkey_file = ${settings.sftpd.hostPrivateKeyFile}"} + ${optionalString (settings.sftpd.accounts.file != null) + "accounts.file = ${settings.sftpd.accounts.file}"} + ${optionalString (settings.sftpd.accounts.url != null) + "accounts.url = ${settings.sftpd.accounts.url}"} ''; }); # Actually require Tahoe, so that we will have it installed. @@ -279,6 +333,12 @@ in serviceConfig = { Type = "simple"; PIDFile = pidfile; + # Believe it or not, Tahoe is very brittle about the order of + # arguments to $(tahoe start). The node directory must come first, + # and arguments which alter Twisted's behavior come afterwards. + ExecStart = '' + ${settings.package}/bin/tahoe start ${nodedir} -n -l- --pidfile=${pidfile} + ''; }; preStart = '' if [ \! -d ${nodedir} ]; then @@ -294,12 +354,6 @@ in # ln -s /etc/tahoe-lafs/${node}.cfg ${nodedir}/tahoe.cfg cp /etc/tahoe-lafs/${node}.cfg ${nodedir}/tahoe.cfg ''; - # Believe it or not, Tahoe is very brittle about the order of - # arguments to $(tahoe start). The node directory must come first, - # and arguments which alter Twisted's behavior come afterwards. - script = '' - tahoe start ${nodedir} -n -l- --pidfile=${pidfile} - ''; }); users.extraUsers = flip mapAttrs' cfg.nodes (node: _: nameValuePair "tahoe.${node}" { diff --git a/nixos/modules/services/network-filesystems/xtreemfs.nix b/nixos/modules/services/network-filesystems/xtreemfs.nix index b051214e1d0..0c6714563d8 100644 --- a/nixos/modules/services/network-filesystems/xtreemfs.nix +++ b/nixos/modules/services/network-filesystems/xtreemfs.nix @@ -153,6 +153,7 @@ in ''; }; extraConfig = mkOption { + type = types.lines; default = ""; example = '' # specify whether SSL is required @@ -173,6 +174,7 @@ in replication = { enable = mkEnableOption "XtreemFS DIR replication plugin"; extraConfig = mkOption { + type = types.lines; example = '' # participants of the replication including this replica babudb.repl.participant.0 = 192.168.0.10 @@ -269,6 +271,7 @@ in ''; }; extraConfig = mkOption { + type = types.lines; example = '' osd_check_interval = 300 no_atime = true @@ -307,6 +310,7 @@ in replication = { enable = mkEnableOption "XtreemFS MRC replication plugin"; extraConfig = mkOption { + type = types.lines; example = '' # participants of the replication including this replica babudb.repl.participant.0 = 192.168.0.10 @@ -385,6 +389,7 @@ in ''; }; extraConfig = mkOption { + type = types.lines; example = '' local_clock_renewal = 0 remote_time_sync = 30000 diff --git a/nixos/modules/services/network-filesystems/yandex-disk.nix b/nixos/modules/services/network-filesystems/yandex-disk.nix index 982b6ca5ea7..4de20664133 100644 --- a/nixos/modules/services/network-filesystems/yandex-disk.nix +++ b/nixos/modules/services/network-filesystems/yandex-disk.nix @@ -55,6 +55,15 @@ in description = "The directory to use for Yandex.Disk storage"; }; + excludes = mkOption { + default = ""; + type = types.string; + example = "data,backup"; + description = '' + Comma-separated list of directories which are excluded from synchronization. + ''; + }; + }; }; @@ -86,7 +95,7 @@ in chown ${u} ${dir} if ! test -d "${cfg.directory}" ; then - mkdir -p -m 755 ${cfg.directory} || + (mkdir -p -m 755 ${cfg.directory} && chown ${u} ${cfg.directory}) || exit 1 fi @@ -94,7 +103,7 @@ in -c '${pkgs.yandex-disk}/bin/yandex-disk token -p ${cfg.password} ${cfg.username} ${dir}/token' ${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${u} \ - -c '${pkgs.yandex-disk}/bin/yandex-disk start --no-daemon -a ${dir}/token -d ${cfg.directory}' + -c '${pkgs.yandex-disk}/bin/yandex-disk start --no-daemon -a ${dir}/token -d ${cfg.directory} --exclude-dirs=${cfg.excludes}' ''; }; diff --git a/nixos/modules/services/networking/amuled.nix b/nixos/modules/services/networking/amuled.nix index bc488d0e910..fc7d56a24fa 100644 --- a/nixos/modules/services/networking/amuled.nix +++ b/nixos/modules/services/networking/amuled.nix @@ -59,7 +59,8 @@ in systemd.services.amuled = { description = "AMule daemon"; - wantedBy = [ "ip-up.target" ]; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; preStart = '' mkdir -p ${cfg.dataDir} diff --git a/nixos/modules/services/networking/asterisk.nix b/nixos/modules/services/networking/asterisk.nix index 13617a1b6c5..5c71a1d8dda 100644 --- a/nixos/modules/services/networking/asterisk.nix +++ b/nixos/modules/services/networking/asterisk.nix @@ -6,29 +6,38 @@ let cfg = config.services.asterisk; asteriskUser = "asterisk"; + asteriskGroup = "asterisk"; varlibdir = "/var/lib/asterisk"; spooldir = "/var/spool/asterisk"; logdir = "/var/log/asterisk"; + # Add filecontents from files of useTheseDefaultConfFiles to confFiles, do not override + defaultConfFiles = subtractLists (attrNames cfg.confFiles) cfg.useTheseDefaultConfFiles; + allConfFiles = + cfg.confFiles // + builtins.listToAttrs (map (x: { name = x; + value = builtins.readFile (pkgs.asterisk + "/etc/asterisk/" + x); }) + defaultConfFiles); + asteriskEtc = pkgs.stdenv.mkDerivation ((mapAttrs' (name: value: nameValuePair # Fudge the names to make bash happy ((replaceChars ["."] ["_"] name) + "_") (value) - ) cfg.confFiles) // + ) allConfFiles) // { confFilesString = concatStringsSep " " ( - attrNames cfg.confFiles + attrNames allConfFiles ); - name = "asterisk.etc"; + name = "asterisk-etc"; # Default asterisk.conf file # (Notice that astetcdir will be set to the path of this derivation) asteriskConf = '' [directories] - astetcdir => @out@ + astetcdir => /etc/asterisk astmoddir => ${pkgs.asterisk}/lib/asterisk/modules astvarlibdir => /var/lib/asterisk astdbdir => /var/lib/asterisk @@ -169,6 +178,16 @@ in ''; }; + useTheseDefaultConfFiles = mkOption { + default = [ "ari.conf" "acl.conf" "agents.conf" "amd.conf" "calendar.conf" "cdr.conf" "cdr_syslog.conf" "cdr_custom.conf" "cel.conf" "cel_custom.conf" "cli_aliases.conf" "confbridge.conf" "dundi.conf" "features.conf" "hep.conf" "iax.conf" "pjsip.conf" "pjsip_wizard.conf" "phone.conf" "phoneprov.conf" "queues.conf" "res_config_sqlite3.conf" "res_parking.conf" "statsd.conf" "udptl.conf" "unistim.conf" ]; + type = types.listOf types.str; + example = [ "sip.conf" "dundi.conf" ]; + description = ''Sets these config files to the default content. The default value for + this option contains all necesscary files to avoid errors at startup. + This does not override settings via <option>services.asterisk.confFiles</option>. + ''; + }; + extraArguments = mkOption { default = []; type = types.listOf types.str; @@ -182,12 +201,22 @@ in }; config = mkIf cfg.enable { - users.extraUsers = singleton - { name = asteriskUser; - uid = config.ids.uids.asterisk; - description = "Asterisk daemon user"; - home = varlibdir; - }; + environment.systemPackages = [ pkgs.asterisk ]; + + environment.etc.asterisk.source = asteriskEtc; + + users.extraUsers.asterisk = + { name = asteriskUser; + group = asteriskGroup; + uid = config.ids.uids.asterisk; + description = "Asterisk daemon user"; + home = varlibdir; + }; + + users.extraGroups.asterisk = + { name = asteriskGroup; + gid = config.ids.gids.asterisk; + }; systemd.services.asterisk = { description = '' @@ -196,14 +225,17 @@ in wantedBy = [ "multi-user.target" ]; + # Do not restart, to avoid disruption of running calls. Restart unit by yourself! + restartIfChanged = false; + preStart = '' # Copy skeleton directory tree to /var for d in '${varlibdir}' '${spooldir}' '${logdir}'; do # TODO: Make exceptions for /var directories that likely should be updated if [ ! -e "$d" ]; then mkdir -p "$d" - cp --recursive ${pkgs.asterisk}/"$d" "$d" - chown --recursive ${asteriskUser} "$d" + cp --recursive ${pkgs.asterisk}/"$d"/* "$d"/ + chown --recursive ${asteriskUser}:${asteriskGroup} "$d" find "$d" -type d | xargs chmod 0755 fi done @@ -215,7 +247,9 @@ in # FIXME: This doesn't account for arguments with spaces argString = concatStringsSep " " cfg.extraArguments; in - "${pkgs.asterisk}/bin/asterisk -U ${asteriskUser} -C ${asteriskEtc}/asterisk.conf ${argString} -F"; + "${pkgs.asterisk}/bin/asterisk -U ${asteriskUser} -C /etc/asterisk/asterisk.conf ${argString} -F"; + ExecReload = ''${pkgs.asterisk}/bin/asterisk -x "core reload" + ''; Type = "forking"; PIDFile = "/var/run/asterisk/asterisk.pid"; }; diff --git a/nixos/modules/services/networking/atftpd.nix b/nixos/modules/services/networking/atftpd.nix index d875ddc6352..e7fd48c99a8 100644 --- a/nixos/modules/services/networking/atftpd.nix +++ b/nixos/modules/services/networking/atftpd.nix @@ -20,13 +20,27 @@ in default = false; type = types.bool; description = '' - Whenever to enable the atftpd TFTP server. + Whether to enable the atftpd TFTP server. By default, the server + binds to address 0.0.0.0. + ''; + }; + + extraOptions = mkOption { + default = []; + type = types.listOf types.str; + example = literalExample '' + [ "--bind-address 192.168.9.1" + "--verbose=7" + ] + ''; + description = '' + Extra command line arguments to pass to atftp. ''; }; root = mkOption { - default = "/var/empty"; - type = types.str; + default = "/srv/tftp"; + type = types.path; description = '' Document root directory for the atftpd. ''; @@ -39,11 +53,11 @@ in config = mkIf cfg.enable { systemd.services.atftpd = { - description = "atftpd TFTP server"; + description = "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}"; + serviceConfig.ExecStart = "${pkgs.atftp}/sbin/atftpd --daemon --no-fork ${lib.concatStringsSep " " cfg.extraOptions} ${cfg.root}"; }; }; diff --git a/nixos/modules/services/networking/avahi-daemon.nix b/nixos/modules/services/networking/avahi-daemon.nix index 7650f45c557..6a786e75bbc 100644 --- a/nixos/modules/services/networking/avahi-daemon.nix +++ b/nixos/modules/services/networking/avahi-daemon.nix @@ -7,10 +7,6 @@ let cfg = config.services.avahi; - # We must escape interfaces due to the systemd interpretation - subsystemDevice = interface: - "sys-subsystem-net-devices-${utils.escapeSystemdPath interface}.device"; - avahiDaemonConf = with cfg; pkgs.writeText "avahi-daemon.conf" '' [server] ${# Users can set `networking.hostName' to the empty string, when getting @@ -75,7 +71,8 @@ in }; browseDomains = mkOption { - default = [ "0pointer.de" "zeroconf.org" ]; + default = [ ]; + example = [ "0pointer.de" "zeroconf.org" ]; description = '' List of non-local DNS domains to be browsed. ''; @@ -178,17 +175,20 @@ in environment.systemPackages = [ pkgs.avahi ]; + systemd.sockets.avahi-daemon = + { description = "Avahi mDNS/DNS-SD Stack Activation Socket"; + listenStreams = [ "/var/run/avahi-daemon/socket" ]; + wantedBy = [ "sockets.target" ]; + }; + systemd.services.avahi-daemon = - let - deps = optionals (cfg.interfaces!=null) (map subsystemDevice cfg.interfaces); - in - { description = "Avahi daemon"; - wantedBy = [ "ip-up.target" ]; - bindsTo = deps; - after = deps; - before = [ "ip-up.target" ]; - # Receive restart event after resume - partOf = [ "post-resume.target" ]; + { description = "Avahi mDNS/DNS-SD Stack"; + wantedBy = [ "multi-user.target" ]; + requires = [ "avahi-daemon.socket" ]; + + serviceConfig."NotifyAccess" = "main"; + serviceConfig."BusName" = "org.freedesktop.Avahi"; + serviceConfig."Type" = "dbus"; path = [ pkgs.coreutils pkgs.avahi ]; diff --git a/nixos/modules/services/networking/bind.nix b/nixos/modules/services/networking/bind.nix index 08afafceff2..0272b6ceff2 100644 --- a/nixos/modules/services/networking/bind.nix +++ b/nixos/modules/services/networking/bind.nix @@ -113,6 +113,7 @@ in }; extraConfig = mkOption { + type = types.lines; default = ""; description = " Extra lines to be added verbatim to the generated named configuration file. @@ -145,8 +146,8 @@ in }; systemd.services.bind = { - description = "BIND name server job"; - after = [ "network-interfaces.target" ]; + description = "BIND Domain Name Server"; + after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; preStart = '' @@ -154,7 +155,8 @@ in chown ${bindUser} /var/run/named ''; - script = "${pkgs.bind.bin}/sbin/named -u ${bindUser} ${optionalString cfg.ipv4Only "-4"} -c ${cfg.configFile} -f"; + script = "${pkgs.bind.out}/sbin/named -u ${bindUser} ${optionalString cfg.ipv4Only "-4"} -c ${cfg.configFile} -f"; + unitConfig.Documentation = "man:named(8)"; }; }; } diff --git a/nixos/modules/services/networking/bird.nix b/nixos/modules/services/networking/bird.nix index e76cdac14ca..1a7a1e24b70 100644 --- a/nixos/modules/services/networking/bird.nix +++ b/nixos/modules/services/networking/bird.nix @@ -1,76 +1,69 @@ { config, lib, pkgs, ... }: let - inherit (lib) mkEnableOption mkIf mkOption singleton types; - inherit (pkgs) bird; - cfg = config.services.bird; - - configFile = pkgs.writeText "bird.conf" '' - ${cfg.config} - ''; -in - -{ - - ###### interface - - options = { - - services.bird = { - - enable = mkEnableOption "BIRD Internet Routing Daemon"; - - config = mkOption { - type = types.string; - description = '' - BIRD Internet Routing Daemon configuration file. - <link xlink:href='http://bird.network.cz/'/> + inherit (lib) mkEnableOption mkIf mkOption types; + + generic = variant: + let + cfg = config.services.${variant}; + pkg = pkgs.${variant}; + birdc = if variant == "bird6" then "birdc6" else "birdc"; + configFile = pkgs.stdenv.mkDerivation { + name = "${variant}.conf"; + text = cfg.config; + preferLocalBuild = true; + buildCommand = '' + echo -n "$text" > $out + ${pkg}/bin/${variant} -d -p -c $out ''; }; - - user = mkOption { - type = types.string; - default = "bird"; - description = '' - BIRD Internet Routing Daemon user. - ''; + in { + ###### interface + options = { + services.${variant} = { + enable = mkEnableOption "BIRD Internet Routing Daemon"; + config = mkOption { + type = types.lines; + description = '' + BIRD Internet Routing Daemon configuration file. + <link xlink:href='http://bird.network.cz/'/> + ''; + }; + }; }; - group = mkOption { - type = types.string; - default = "bird"; - description = '' - BIRD Internet Routing Daemon group. - ''; + ###### implementation + config = mkIf cfg.enable { + environment.systemPackages = [ pkg ]; + systemd.services.${variant} = { + description = "BIRD Internet Routing Daemon"; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "forking"; + Restart = "on-failure"; + ExecStart = "${pkg}/bin/${variant} -c ${configFile} -u ${variant} -g ${variant}"; + ExecReload = "${pkg}/bin/${birdc} configure"; + ExecStop = "${pkg}/bin/${birdc} down"; + CapabilityBoundingSet = [ "CAP_CHOWN" "CAP_FOWNER" "CAP_DAC_OVERRIDE" "CAP_SETUID" "CAP_SETGID" + # see bird/sysdep/linux/syspriv.h + "CAP_NET_BIND_SERVICE" "CAP_NET_BROADCAST" "CAP_NET_ADMIN" "CAP_NET_RAW" ]; + ProtectSystem = "full"; + ProtectHome = "yes"; + SystemCallFilter="~@cpu-emulation @debug @keyring @module @mount @obsolete @raw-io"; + MemoryDenyWriteExecute = "yes"; + }; + }; + users = { + extraUsers.${variant} = { + description = "BIRD Internet Routing Daemon user"; + group = "${variant}"; + }; + extraGroups.${variant} = {}; + }; }; - }; - }; - - - ###### implementation - - config = mkIf cfg.enable { - - users.extraUsers = singleton { - name = cfg.user; - description = "BIRD Internet Routing Daemon user"; - uid = config.ids.uids.bird; - group = cfg.group; - }; - - users.extraGroups = singleton { - name = cfg.group; - gid = config.ids.gids.bird; - }; - - systemd.services.bird = { - description = "BIRD Internet Routing Daemon"; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - ExecStart = "${bird}/bin/bird -d -c ${configFile} -s /var/run/bird.ctl -u ${cfg.user} -g ${cfg.group}"; - }; - }; - }; + inherit (config.services) bird bird6; +in { + imports = [(generic "bird") (generic "bird6")]; } diff --git a/nixos/modules/services/networking/bitlbee.nix b/nixos/modules/services/networking/bitlbee.nix index 5e6847097a9..e72ea20ccce 100644 --- a/nixos/modules/services/networking/bitlbee.nix +++ b/nixos/modules/services/networking/bitlbee.nix @@ -7,11 +7,6 @@ let cfg = config.services.bitlbee; bitlbeeUid = config.ids.uids.bitlbee; - authModeCheck = v: - v == "Open" || - v == "Closed" || - v == "Registered"; - bitlbeeConfig = pkgs.writeText "bitlbee.conf" '' [settings] @@ -67,7 +62,7 @@ in authMode = mkOption { default = "Open"; - type = types.addCheck types.str authModeCheck; + type = types.enum [ "Open" "Closed" "Registered" ]; description = '' The following authentication modes are available: Open -- Accept connections from anyone, use NickServ for user authentication. diff --git a/nixos/modules/services/networking/chrony.nix b/nixos/modules/services/networking/chrony.nix index a38142b4a08..f2ff11633b1 100644 --- a/nixos/modules/services/networking/chrony.nix +++ b/nixos/modules/services/networking/chrony.nix @@ -31,7 +31,7 @@ in }; servers = mkOption { - default = config.services.ntp.servers; + default = config.networking.timeServers; description = '' The set of NTP servers from which to synchronise. ''; @@ -51,6 +51,7 @@ in }; extraConfig = mkOption { + type = types.lines; default = ""; description = '' Extra configuration directives that should be added to @@ -101,7 +102,7 @@ in home = stateDir; }; - systemd.services.ntpd.enable = mkForce false; + systemd.services.timesyncd.enable = mkForce false; systemd.services.chronyd = { description = "chrony NTP daemon"; diff --git a/nixos/modules/services/networking/cjdns-hosts.sh b/nixos/modules/services/networking/cjdns-hosts.sh deleted file mode 100644 index 8a2b47e5214..00000000000 --- a/nixos/modules/services/networking/cjdns-hosts.sh +++ /dev/null @@ -1,11 +0,0 @@ -pubs=($pubs) -hosts=($hosts) - -lines="''\n" -for ((i = 0; i < ${#pubs[*]}; i++)); do - addr=$($cjdns/bin/publictoip6 ${pubs[i]}) - lines="${lines}$addr ${hosts[i]}\n" -done -lines="${lines}''" - -echo -ne $lines > $out diff --git a/nixos/modules/services/networking/cjdns.nix b/nixos/modules/services/networking/cjdns.nix index 0495b32c6fa..a10851c1652 100644 --- a/nixos/modules/services/networking/cjdns.nix +++ b/nixos/modules/services/networking/cjdns.nix @@ -28,21 +28,18 @@ let }; }; - peers = mapAttrsToList (n: v: v) (cfg.ETHInterface.connectTo // cfg.UDPInterface.connectTo); - - pubs = toString (map (p: if p.hostname == "" then "" else p.publicKey) peers); - hosts = toString (map (p: if p.hostname == "" then "" else p.hostname) peers); - - cjdnsHosts = - if hosts != "" then - import (pkgs.stdenv.mkDerivation { - name = "cjdns-hosts"; - builder = ./cjdns-hosts.sh; - - inherit (pkgs) cjdns; - inherit pubs hosts; - }) - else ""; + # Additional /etc/hosts entries for peers with an associated hostname + cjdnsExtraHosts = import (pkgs.runCommand "cjdns-hosts" {} + # Generate a builder that produces an output usable as a Nix string value + '' + exec >$out + echo \'\' + ${concatStringsSep "\n" (mapAttrsToList (k: v: + optionalString (v.hostname != "") + "echo $(${pkgs.cjdns}/bin/publictoip6 ${v.publicKey}) ${v.hostname}") + (cfg.ETHInterface.connectTo // cfg.UDPInterface.connectTo))} + echo \'\' + ''); parseModules = x: x // { connectTo = mapAttrs (name: value: { inherit (value) password publicKey; }) x.connectTo; }; @@ -95,8 +92,8 @@ in }; confFile = mkOption { - type = types.str; - default = ""; + type = types.nullOr types.path; + default = null; example = "/etc/cjdroute.conf"; description = '' Ignore all other cjdns options and load configuration from this file. @@ -112,14 +109,14 @@ in "49275fut6tmzu354pq70sr5b95qq0vj" ]; description = '' - Any remote cjdns nodes that offer these passwords on + Any remote cjdns nodes that offer these passwords on connection will be allowed to route through this node. ''; }; - + admin = { bind = mkOption { - type = types.string; + type = types.str; default = "127.0.0.1:11234"; description = '' Bind the administration port to this address and port. @@ -129,7 +126,7 @@ in UDPInterface = { bind = mkOption { - type = types.string; + type = types.str; default = ""; example = "192.168.1.32:43211"; description = '' @@ -154,6 +151,7 @@ in ETHInterface = { bind = mkOption { + type = types.str; default = ""; example = "eth0"; description = @@ -197,22 +195,33 @@ in }; }; + addExtraHosts = mkOption { + type = types.bool; + default = false; + description = '' + Whether to add cjdns peers with an associated hostname to + <filename>/etc/hosts</filename>. Beware that enabling this + incurs heavy eval-time costs. + ''; + }; + }; }; - config = mkIf config.services.cjdns.enable { + config = mkIf cfg.enable { boot.kernelModules = [ "tun" ]; # networking.firewall.allowedUDPPorts = ... systemd.services.cjdns = { - description = "encrypted networking for everybody"; - wantedBy = [ "network.target" ]; - after = [ "networkSetup.service" "network-interfaces.target" ]; + description = "cjdns: routing engine designed for security, scalability, speed and ease of use"; + wantedBy = [ "multi-user.target" "sleep.target"]; + after = [ "network-online.target" ]; + bindsTo = [ "network-online.target" ]; - preStart = if cfg.confFile != "" then "" else '' + preStart = if cfg.confFile != null then "" else '' [ -e /etc/cjdns.keys ] && source /etc/cjdns.keys if [ -z "$CJDNS_PRIVATE_KEY" ]; then @@ -228,13 +237,13 @@ in fi if [ -z "$CJDNS_ADMIN_PASSWORD" ]; then - echo "CJDNS_ADMIN_PASSWORD=$(${pkgs.coreutils}/bin/head -c 96 /dev/urandom | ${pkgs.coreutils}/bin/tr -dc A-Za-z0-9)" \ + echo "CJDNS_ADMIN_PASSWORD=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 96)" \ >> /etc/cjdns.keys fi ''; script = ( - if cfg.confFile != "" then "${pkg}/bin/cjdroute < ${cfg.confFile}" else + if cfg.confFile != null then "${pkg}/bin/cjdroute < ${cfg.confFile}" else '' source /etc/cjdns.keys echo '${cjdrouteConf}' | sed \ @@ -246,14 +255,22 @@ in serviceConfig = { Type = "forking"; - Restart = "on-failure"; + Restart = "always"; + StartLimitInterval = 0; + RestartSec = 1; + CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_RAW"; + AmbientCapabilities = "CAP_NET_ADMIN CAP_NET_RAW"; + ProtectSystem = "full"; + MemoryDenyWriteExecute = true; + ProtectHome = true; + PrivateTmp = true; }; }; - networking.extraHosts = "${cjdnsHosts}"; + networking.extraHosts = mkIf cfg.addExtraHosts cjdnsExtraHosts; assertions = [ - { assertion = ( cfg.ETHInterface.bind != "" || cfg.UDPInterface.bind != "" || cfg.confFile != "" ); + { assertion = ( cfg.ETHInterface.bind != "" || cfg.UDPInterface.bind != "" || cfg.confFile != null ); message = "Neither cjdns.ETHInterface.bind nor cjdns.UDPInterface.bind defined."; } { assertion = config.networking.enableIPv6; diff --git a/nixos/modules/services/networking/cntlm.nix b/nixos/modules/services/networking/cntlm.nix index 76c0fd7d0ea..890ff508407 100644 --- a/nixos/modules/services/networking/cntlm.nix +++ b/nixos/modules/services/networking/cntlm.nix @@ -61,6 +61,7 @@ in }; extraConfig = mkOption { + type = types.lines; default = ""; description = "Verbatim contents of <filename>cntlm.conf</filename>."; }; diff --git a/nixos/modules/services/networking/connman.nix b/nixos/modules/services/networking/connman.nix index 3fecfbb13a0..d0683b87780 100644 --- a/nixos/modules/services/networking/connman.nix +++ b/nixos/modules/services/networking/connman.nix @@ -27,6 +27,14 @@ in { ''; }; + enableVPN = mkOption { + type = types.bool; + default = true; + description = '' + Whether to enable ConnMan VPN service. + ''; + }; + extraConfig = mkOption { type = types.lines; default = '' @@ -78,7 +86,7 @@ in { }; }; - systemd.services."connman-vpn" = { + systemd.services."connman-vpn" = mkIf cfg.enableVPN { description = "ConnMan VPN service"; wantedBy = [ "multi-user.target" ]; after = [ "syslog.target" ]; @@ -91,7 +99,7 @@ in { }; }; - systemd.services."net-connman-vpn" = { + systemd.services."net-connman-vpn" = mkIf cfg.enableVPN { description = "D-BUS Service"; serviceConfig = { Name = "net.connman.vpn"; diff --git a/nixos/modules/services/networking/dante.nix b/nixos/modules/services/networking/dante.nix new file mode 100644 index 00000000000..a9a77f3412a --- /dev/null +++ b/nixos/modules/services/networking/dante.nix @@ -0,0 +1,61 @@ +{ config, lib, pkgs, ... }: +with lib; + +let + cfg = config.services.dante; + confFile = pkgs.writeText "dante-sockd.conf" '' + user.privileged: root + user.unprivileged: dante + + ${cfg.config} + ''; +in + +{ + meta = { + maintainers = with maintainers; [ arobyn ]; + }; + + options = { + services.dante = { + enable = mkEnableOption "Dante SOCKS proxy"; + + config = mkOption { + default = null; + type = types.nullOr types.str; + description = '' + Contents of Dante's configuration file + NOTE: user.privileged/user.unprivileged are set by the service + ''; + }; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + { assertion = cfg.config != null; + message = "please provide Dante configuration file contents"; + } + ]; + + users.users.dante = { + description = "Dante SOCKS proxy daemon user"; + isSystemUser = true; + group = "dante"; + }; + users.groups.dante = {}; + + systemd.services.dante = { + description = "Dante SOCKS v4 and v5 compatible proxy server"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + Type = "simple"; + ExecStart = "${pkgs.dante}/bin/sockd -f ${confFile}"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + Restart = "always"; + }; + }; + }; +} diff --git a/nixos/modules/services/networking/ddclient.nix b/nixos/modules/services/networking/ddclient.nix index 005c57dce7c..d1900deceaf 100644 --- a/nixos/modules/services/networking/ddclient.nix +++ b/nixos/modules/services/networking/ddclient.nix @@ -7,7 +7,7 @@ let stateDir = "/var/spool/ddclient"; ddclientUser = "ddclient"; - ddclientFlags = "-foreground -verbose -noquiet -file /etc/ddclient.conf"; + ddclientFlags = "-foreground -verbose -noquiet -file ${config.services.ddclient.configFile}"; ddclientPIDFile = "${stateDir}/ddclient.pid"; in @@ -52,6 +52,17 @@ in ''; }; + configFile = mkOption { + default = "/etc/ddclient.conf"; + type = path; + description = '' + Path to configuration file. + When set to the default '/etc/ddclient.conf' it will be populated with the various other options in this module. When it is changed (for example: '/root/nixos/secrets/ddclient.conf') the file read directly to configure ddclient. This is a source of impurity. + The purpose of this is to avoid placing secrets into the store. + ''; + example = "/root/nixos/secrets/ddclient.conf"; + }; + protocol = mkOption { default = "dyndns2"; type = str; @@ -78,7 +89,7 @@ in extraConfig = mkOption { default = ""; - type = str; + type = lines; description = '' Extra configuration. Contents will be added verbatim to the configuration file. ''; @@ -88,7 +99,7 @@ in default = "web, web=checkip.dyndns.com/, web-skip='Current IP Address: '"; type = str; description = '' - Method to determine the IP address to send to the dymanic DNS provider. + Method to determine the IP address to send to the dynamic DNS provider. ''; }; }; @@ -109,9 +120,11 @@ in }; environment.etc."ddclient.conf" = { + enable = config.services.ddclient.configFile == "/etc/ddclient.conf"; uid = config.ids.uids.ddclient; mode = "0600"; text = '' + # This file can be used as a template for configFile or is automatically generated by Nix options. daemon=600 cache=${stateDir}/ddclient.cache pid=${ddclientPIDFile} diff --git a/nixos/modules/services/networking/dhcpcd.nix b/nixos/modules/services/networking/dhcpcd.nix index b31d479ab4f..87c0aa50a1f 100644 --- a/nixos/modules/services/networking/dhcpcd.nix +++ b/nixos/modules/services/networking/dhcpcd.nix @@ -10,7 +10,8 @@ let interfaces = attrValues config.networking.interfaces; - enableDHCP = config.networking.useDHCP || any (i: i.useDHCP == true) interfaces; + enableDHCP = config.networking.dhcpcd.enable && + (config.networking.useDHCP || any (i: i.useDHCP == true) interfaces); # Don't start dhcpcd on explicitly configured interfaces or on # interfaces that are part of a bridge, bond or sit device. @@ -61,7 +62,6 @@ let ${cfg.extraConfig} ''; - # Hook for emitting ip-up/ip-down events. exitHook = pkgs.writeText "dhcpcd.exit-hook" '' if [ "$reason" = BOUND -o "$reason" = REBOOT ]; then @@ -73,14 +73,8 @@ let # applies to openntpd. ${config.systemd.package}/bin/systemctl try-restart ntpd.service ${config.systemd.package}/bin/systemctl try-restart openntpd.service - - ${config.systemd.package}/bin/systemctl start ip-up.target fi - #if [ "$reason" = EXPIRE -o "$reason" = RELEASE -o "$reason" = NOCARRIER ] ; then - # ${config.systemd.package}/bin/systemctl start ip-down.target - #fi - ${cfg.runHook} ''; @@ -92,6 +86,15 @@ in options = { + networking.dhcpcd.enable = mkOption { + type = types.bool; + default = true; + description = '' + Whether to enable dhcpcd for device configuration. This is mainly to + explicitly disable dhcpcd (for example when using networkd). + ''; + }; + networking.dhcpcd.persistent = mkOption { type = types.bool; default = false; @@ -154,10 +157,9 @@ in systemd.services.dhcpcd = { description = "DHCP Client"; - wantedBy = [ "network.target" ]; - # Work-around to deal with problems where the kernel would remove & - # re-create Wifi interfaces early during boot. - after = [ "network-interfaces.target" ]; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + wants = [ "network.target" ]; # Stopping dhcpcd during a reconfiguration is undesirable # because it brings down the network interfaces configured by diff --git a/nixos/modules/services/networking/dhcpd.nix b/nixos/modules/services/networking/dhcpd.nix index 900df67b53a..d2cd00e74a1 100644 --- a/nixos/modules/services/networking/dhcpd.nix +++ b/nixos/modules/services/networking/dhcpd.nix @@ -47,6 +47,7 @@ in }; extraConfig = mkOption { + type = types.lines; default = ""; example = '' option subnet-mask 255.255.255.0; @@ -66,6 +67,14 @@ in "; }; + extraFlags = mkOption { + default = ""; + example = "-6"; + description = " + Additional command line flags to be passed to the dhcpd daemon. + "; + }; + configFile = mkOption { default = null; description = " @@ -138,6 +147,7 @@ in { ExecStart = "@${pkgs.dhcp}/sbin/dhcpd dhcpd" + " -pf /run/dhcpd/dhcpd.pid -cf ${configFile}" + " -lf ${stateDir}/dhcpd.leases -user dhcpd -group nogroup" + + " ${cfg.extraFlags}" + " ${toString cfg.interfaces}"; Restart = "always"; Type = "forking"; diff --git a/nixos/modules/services/networking/dnscrypt-proxy.nix b/nixos/modules/services/networking/dnscrypt-proxy.nix index 2714e8d7599..462039803f8 100644 --- a/nixos/modules/services/networking/dnscrypt-proxy.nix +++ b/nixos/modules/services/networking/dnscrypt-proxy.nix @@ -5,15 +5,25 @@ let apparmorEnabled = config.security.apparmor.enable; dnscrypt-proxy = pkgs.dnscrypt-proxy; cfg = config.services.dnscrypt-proxy; + stateDirectory = "/var/lib/dnscrypt-proxy"; localAddress = "${cfg.localAddress}:${toString cfg.localPort}"; - daemonArgs = - [ "--local-address=${localAddress}" - (optionalString cfg.tcpOnly "--tcp-only") - (optionalString cfg.ephemeralKeys "-E") - ] - ++ resolverArgs; + # The minisign public key used to sign the upstream resolver list. + # This is somewhat more flexible than preloading the key as an + # embedded string. + upstreamResolverListPubKey = pkgs.fetchurl { + url = https://raw.githubusercontent.com/jedisct1/dnscrypt-proxy/master/minisign.pub; + sha256 = "18lnp8qr6ghfc2sd46nn1rhcpr324fqlvgsp4zaigw396cd7vnnh"; + }; + + # Internal flag indicating whether the upstream resolver list is used + useUpstreamResolverList = cfg.resolverList == null && cfg.customResolver == null; + + resolverList = + if (cfg.resolverList != null) + then cfg.resolverList + else "${stateDirectory}/dnscrypt-resolvers.csv"; resolverArgs = if (cfg.customResolver != null) then @@ -22,9 +32,16 @@ let "--provider-key=${cfg.customResolver.key}" ] else - [ "--resolvers-list=${cfg.resolverList}" - "--resolver-name=${toString cfg.resolverName}" + [ "--resolvers-list=${resolverList}" + "--resolver-name=${cfg.resolverName}" ]; + + # The final command line arguments passed to the daemon + daemonArgs = + [ "--local-address=${localAddress}" ] + ++ optional cfg.tcpOnly "--tcp-only" + ++ optional cfg.ephemeralKeys "-E" + ++ resolverArgs; in { @@ -35,7 +52,11 @@ in options = { services.dnscrypt-proxy = { - enable = mkEnableOption "DNSCrypt client proxy"; + enable = mkOption { + default = false; + type = types.bool; + description = "Whether to enable the DNSCrypt client proxy"; + }; localAddress = mkOption { default = "127.0.0.1"; @@ -62,24 +83,20 @@ in default = "dnscrypt.eu-nl"; type = types.nullOr types.str; description = '' - The name of the upstream DNSCrypt resolver to use, taken from the - list named in the <literal>resolverList</literal> option. - The default resolver is located in Holland, supports DNS security - extensions, and claims to not keep logs. + The name of the upstream DNSCrypt resolver to use, taken from + <filename>${resolverList}</filename>. The default resolver is + located in Holland, supports DNS security extensions, and + <emphasis>claims</emphasis> to not keep logs. ''; }; resolverList = mkOption { + default = null; + type = types.nullOr types.path; description = '' - The list of upstream DNSCrypt resolvers. By default, we use the most - recent list published by upstream. + List of DNSCrypt resolvers. The default is to use the list of + public resolvers provided by upstream. ''; - example = literalExample "${pkgs.dnscrypt-proxy}/share/dnscrypt-proxy/dnscrypt-resolvers.csv"; - default = pkgs.fetchurl { - url = https://raw.githubusercontent.com/jedisct1/dnscrypt-proxy/master/dnscrypt-resolvers.csv; - sha256 = "1i9wzw4zl052h5nyp28bwl8d66cgj0awvjhw5wgwz0warkjl1g8g"; - }; - defaultText = "pkgs.fetchurl { url = ...; sha256 = ...; }"; }; customResolver = mkOption { @@ -146,7 +163,7 @@ in } ]; - security.apparmor.profiles = mkIf apparmorEnabled (singleton (pkgs.writeText "apparmor-dnscrypt-proxy" '' + security.apparmor.profiles = optional apparmorEnabled (pkgs.writeText "apparmor-dnscrypt-proxy" '' ${dnscrypt-proxy}/bin/dnscrypt-proxy { /dev/null rw, /dev/urandom r, @@ -173,9 +190,9 @@ in ${getLib pkgs.lz4}/lib/liblz4.so.* mr, ${getLib pkgs.attr}/lib/libattr.so.* mr, - ${cfg.resolverList} r, + ${resolverList} r, } - '')); + ''); users.users.dnscrypt-proxy = { description = "dnscrypt-proxy daemon user"; @@ -184,11 +201,61 @@ in }; users.groups.dnscrypt-proxy = {}; + systemd.services.init-dnscrypt-proxy-statedir = optionalAttrs useUpstreamResolverList { + description = "Initialize dnscrypt-proxy state directory"; + script = '' + mkdir -pv ${stateDirectory} + chown -c dnscrypt-proxy:dnscrypt-proxy ${stateDirectory} + cp --preserve=timestamps -uv \ + ${pkgs.dnscrypt-proxy}/share/dnscrypt-proxy/dnscrypt-resolvers.csv \ + ${stateDirectory} + ''; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + }; + + systemd.services.update-dnscrypt-resolvers = optionalAttrs useUpstreamResolverList { + description = "Update list of DNSCrypt resolvers"; + + requires = [ "init-dnscrypt-proxy-statedir.service" ]; + after = [ "init-dnscrypt-proxy-statedir.service" ]; + + path = with pkgs; [ curl minisign ]; + script = '' + cd ${stateDirectory} + curl -fSsL -o dnscrypt-resolvers.csv.tmp \ + https://download.dnscrypt.org/dnscrypt-proxy/dnscrypt-resolvers.csv + curl -fSsL -o dnscrypt-resolvers.csv.minisig.tmp \ + https://download.dnscrypt.org/dnscrypt-proxy/dnscrypt-resolvers.csv.minisig + mv dnscrypt-resolvers.csv.minisig{.tmp,} + minisign -q -V -p ${upstreamResolverListPubKey} \ + -m dnscrypt-resolvers.csv.tmp -x dnscrypt-resolvers.csv.minisig + mv dnscrypt-resolvers.csv{.tmp,} + ''; + + serviceConfig = { + PrivateTmp = true; + PrivateDevices = true; + ProtectHome = true; + ProtectSystem = true; + }; + }; + + systemd.timers.update-dnscrypt-resolvers = optionalAttrs useUpstreamResolverList { + timerConfig = { + OnBootSec = "5min"; + OnUnitActiveSec = "6h"; + }; + wantedBy = [ "timers.target" ]; + }; + systemd.sockets.dnscrypt-proxy = { description = "dnscrypt-proxy listening socket"; socketConfig = { - ListenStream = "${localAddress}"; - ListenDatagram = "${localAddress}"; + ListenStream = localAddress; + ListenDatagram = localAddress; }; wantedBy = [ "sockets.target" ]; }; @@ -196,8 +263,15 @@ in systemd.services.dnscrypt-proxy = { description = "dnscrypt-proxy daemon"; - after = [ "network.target" ] ++ optional apparmorEnabled "apparmor.service"; - requires = [ "dnscrypt-proxy.socket "] ++ optional apparmorEnabled "apparmor.service"; + before = [ "nss-lookup.target" ]; + + after = [ "network.target" ] + ++ optional apparmorEnabled "apparmor.service" + ++ optional useUpstreamResolverList "init-dnscrypt-proxy-statedir.service"; + + requires = [ "dnscrypt-proxy.socket "] + ++ optional apparmorEnabled "apparmor.service" + ++ optional useUpstreamResolverList "init-dnscrypt-proxy-statedir.service"; serviceConfig = { Type = "simple"; diff --git a/nixos/modules/services/networking/dnscrypt-proxy.xml b/nixos/modules/services/networking/dnscrypt-proxy.xml index e212a8d3e2c..982961833ad 100644 --- a/nixos/modules/services/networking/dnscrypt-proxy.xml +++ b/nixos/modules/services/networking/dnscrypt-proxy.xml @@ -49,8 +49,8 @@ <para> <programlisting> { - services.dnsmasq.enable = true; - services.dnsmasq.servers = [ "127.0.0.1#43" ]; + services.dnsmasq.enable = true; + services.dnsmasq.servers = [ "127.0.0.1#43" ]; } </programlisting> </para> @@ -60,12 +60,9 @@ <para> <programlisting> { - networking.nameservers = [ "127.0.0.1" ]; - services.unbound.enable = true; - services.unbound.forwardAddresses = [ "127.0.0.1@43" ]; - services.unbound.extraConfig = '' - do-not-query-localhost: no - ''; + networking.nameservers = [ "127.0.0.1" ]; + services.unbound.enable = true; + services.unbound.forwardAddresses = [ "127.0.0.1@43" ]; } </programlisting> </para> diff --git a/nixos/modules/services/networking/docker-registry-server.nix b/nixos/modules/services/networking/docker-registry-server.nix deleted file mode 100644 index d21bbb6a86c..00000000000 --- a/nixos/modules/services/networking/docker-registry-server.nix +++ /dev/null @@ -1,98 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - cfg = config.services.nodeDockerRegistry; - -in { - options.services.nodeDockerRegistry = { - enable = mkEnableOption "docker registry service"; - - port = mkOption { - description = "Docker registry listening port."; - default = 8080; - type = types.int; - }; - - users = mkOption { - description = "Docker registry list of users."; - default = []; - options = [{ - user = mkOption { - description = "Docker registry user username."; - type = types.str; - }; - - pass = mkOption { - description = "Docker registry user password."; - type = types.str; - }; - }]; - type = types.listOf types.optionSet; - }; - - onTag = mkOption { - description = "Docker registry hook triggered when an image is tagged."; - default = ""; - type = types.str; - }; - - onImage = mkOption { - description = "Docker registry hook triggered when an image metadata is uploaded."; - default = ""; - type = types.str; - }; - - onLayer = mkOption { - description = "Docker registry hook triggered when an when an image layer is uploaded."; - default = ""; - type = types.str; - }; - - onVerify = mkOption { - description = "Docker registry hook triggered when an image layer+metadata has been verified."; - default = ""; - type = types.str; - }; - - onIndex = mkOption { - description = "Docker registry hook triggered when an when an image file system data has been indexed."; - default = ""; - type = types.str; - }; - - dataDir = mkOption { - description = "Docker registry data directory"; - default = "/var/lib/docker-registry"; - type = types.path; - }; - }; - - config = mkIf cfg.enable { - systemd.services.docker-registry-server = { - description = "Docker Registry Service."; - wantedBy = ["multi-user.target"]; - after = ["network.target"]; - script = '' - ${pkgs.nodePackages.docker-registry-server}/bin/docker-registry-server \ - --dir ${cfg.dataDir} \ - --port ${toString cfg.port} \ - ${concatMapStringsSep " " (u: "--user ${u.user}:${u.pass}") cfg.users} \ - ${optionalString (cfg.onTag != "") "--on-tag '${cfg.onTag}'"} \ - ${optionalString (cfg.onImage != "") "--on-image '${cfg.onImage}'"} \ - ${optionalString (cfg.onVerify != "") "--on-verify '${cfg.onVerify}'"} \ - ${optionalString (cfg.onIndex != "") "--on-index '${cfg.onIndex}'"} - ''; - - serviceConfig.User = "docker-registry"; - }; - - users.extraUsers.docker-registry = { - uid = config.ids.uids.docker-registry; - description = "Docker registry user"; - createHome = true; - home = cfg.dataDir; - }; - }; -} diff --git a/nixos/modules/services/networking/fakeroute.nix b/nixos/modules/services/networking/fakeroute.nix new file mode 100644 index 00000000000..82a9fb729d8 --- /dev/null +++ b/nixos/modules/services/networking/fakeroute.nix @@ -0,0 +1,63 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.fakeroute; + routeConf = pkgs.writeText "route.conf" (concatStringsSep "\n" cfg.route); + +in + +{ + + ###### interface + + options = { + + services.fakeroute = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable the fakeroute service. + ''; + }; + + route = mkOption { + type = types.listOf types.str; + default = []; + example = [ + "216.102.187.130" + "4.0.1.122" + "198.116.142.34" + "63.199.8.242" + ]; + description = '' + Fake route that will appear after the real + one to any host running a traceroute. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + systemd.services.fakeroute = { + description = "Fakeroute Daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "forking"; + User = "root"; + ExecStart = "${pkgs.fakeroute}/bin/fakeroute -f ${routeConf}"; + }; + }; + + }; + +} diff --git a/nixos/modules/services/networking/ferm.nix b/nixos/modules/services/networking/ferm.nix index 6271e82541f..8933e166f59 100644 --- a/nixos/modules/services/networking/ferm.nix +++ b/nixos/modules/services/networking/ferm.nix @@ -51,6 +51,7 @@ in { before = [ "network-pre.target" ]; wants = [ "network-pre.target" ]; wantedBy = [ "multi-user.target" ]; + reloadIfChanged = true; serviceConfig = { Type="oneshot"; RemainAfterExit = "yes"; diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix index 138153306dd..1c0ea5034df 100644 --- a/nixos/modules/services/networking/firewall.nix +++ b/nixos/modules/services/networking/firewall.nix @@ -100,13 +100,13 @@ let # Perform a reverse-path test to refuse spoofers # For now, we just drop, as the raw table doesn't have a log-refuse yet - ${optionalString (kernelHasRPFilter && cfg.checkReversePath) '' + ${optionalString (kernelHasRPFilter && (cfg.checkReversePath != false)) '' # Clean up rpfilter rules ip46tables -t raw -D PREROUTING -j nixos-fw-rpfilter 2> /dev/null || true ip46tables -t raw -F nixos-fw-rpfilter 2> /dev/null || true ip46tables -t raw -N nixos-fw-rpfilter 2> /dev/null || true - ip46tables -t raw -A nixos-fw-rpfilter -m rpfilter -j RETURN + ip46tables -t raw -A nixos-fw-rpfilter -m rpfilter ${optionalString (cfg.checkReversePath == "loose") "--loose"} -j RETURN # Allows this host to act as a DHCPv4 server iptables -t raw -A nixos-fw-rpfilter -s 0.0.0.0 -d 255.255.255.255 -p udp --sport 68 --dport 67 -j RETURN @@ -200,7 +200,7 @@ let # Clean up after added ruleset ip46tables -D INPUT -j nixos-fw 2>/dev/null || true - ${optionalString (kernelHasRPFilter && cfg.checkReversePath) '' + ${optionalString (kernelHasRPFilter && (cfg.checkReversePath != false)) '' ip46tables -t raw -D PREROUTING -j nixos-fw-rpfilter 2>/dev/null || true ''} @@ -373,7 +373,7 @@ in networking.firewall.checkReversePath = mkOption { default = kernelHasRPFilter; - type = types.bool; + type = types.either types.bool (types.enum ["strict" "loose"]); description = '' Performs a reverse path filter test on a packet. @@ -381,7 +381,8 @@ in that the packet arrived on, it is refused. If using asymmetric routing or other complicated routing, - disable this setting and setup your own counter-measures. + set this option to loose mode or disable it and setup your + own counter-measures. (needs kernel 3.3+) ''; @@ -482,7 +483,7 @@ in options nf_conntrack nf_conntrack_helper=0 ''; - assertions = [ { assertion = ! cfg.checkReversePath || kernelHasRPFilter; + assertions = [ { assertion = (cfg.checkReversePath != false) || kernelHasRPFilter; message = "This kernel does not support rpfilter"; } { assertion = cfg.autoLoadConntrackHelpers || kernelCanDisableHelpers; message = "This kernel does not support disabling conntrack helpers"; } @@ -490,7 +491,8 @@ in systemd.services.firewall = { description = "Firewall"; - wantedBy = [ "network-pre.target" ]; + wantedBy = [ "sysinit.target" ]; + wants = [ "network-pre.target" ]; before = [ "network-pre.target" ]; after = [ "systemd-modules-load.service" ]; @@ -500,6 +502,7 @@ in # containers don't have CAP_SYS_MODULE. So the host system had # better have all necessary modules already loaded. unitConfig.ConditionCapability = "CAP_NET_ADMIN"; + unitConfig.DefaultDependencies = false; reloadIfChanged = true; diff --git a/nixos/modules/services/networking/flannel.nix b/nixos/modules/services/networking/flannel.nix new file mode 100644 index 00000000000..ca47a18bc1f --- /dev/null +++ b/nixos/modules/services/networking/flannel.nix @@ -0,0 +1,154 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.flannel; + + networkConfig = filterAttrs (n: v: v != null) { + Network = cfg.network; + SubnetLen = cfg.subnetLen; + SubnetMin = cfg.subnetMin; + SubnetMax = cfg.subnetMax; + Backend = cfg.backend; + }; +in { + options.services.flannel = { + enable = mkEnableOption "flannel"; + + package = mkOption { + description = "Package to use for flannel"; + type = types.package; + default = pkgs.flannel.bin; + defaultText = "pkgs.flannel.bin"; + }; + + publicIp = mkOption { + description = '' + IP accessible by other nodes for inter-host communication. + Defaults to the IP of the interface being used for communication. + ''; + type = types.nullOr types.str; + default = null; + }; + + iface = mkOption { + description = '' + Interface to use (IP or name) for inter-host communication. + Defaults to the interface for the default route on the machine. + ''; + type = types.nullOr types.str; + default = null; + }; + + etcd = { + endpoints = mkOption { + description = "Etcd endpoints"; + type = types.listOf types.str; + default = ["http://127.0.0.1:2379"]; + }; + + prefix = mkOption { + description = "Etcd key prefix"; + type = types.str; + default = "/coreos.com/network"; + }; + + caFile = mkOption { + description = "Etcd certificate authority file"; + type = types.nullOr types.path; + default = null; + }; + + certFile = mkOption { + description = "Etcd cert file"; + type = types.nullOr types.path; + default = null; + }; + + keyFile = mkOption { + description = "Etcd key file"; + type = types.nullOr types.path; + default = null; + }; + }; + + network = mkOption { + description = " IPv4 network in CIDR format to use for the entire flannel network."; + type = types.str; + }; + + subnetLen = mkOption { + description = '' + The size of the subnet allocated to each host. Defaults to 24 (i.e. /24) + unless the Network was configured to be smaller than a /24 in which case + it is one less than the network. + ''; + type = types.int; + default = 24; + }; + + subnetMin = mkOption { + description = '' + The beginning of IP range which the subnet allocation should start with. + Defaults to the first subnet of Network. + ''; + type = types.nullOr types.str; + default = null; + }; + + subnetMax = mkOption { + description = '' + The end of IP range which the subnet allocation should start with. + Defaults to the last subnet of Network. + ''; + type = types.nullOr types.str; + default = null; + }; + + backend = mkOption { + description = "Type of backend to use and specific configurations for that backend."; + type = types.attrs; + default = { + Type = "vxlan"; + }; + }; + }; + + config = mkIf cfg.enable { + systemd.services.flannel = { + description = "Flannel Service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + environment = { + FLANNELD_PUBLIC_IP = cfg.publicIp; + FLANNELD_ETCD_ENDPOINTS = concatStringsSep "," cfg.etcd.endpoints; + FLANNELD_ETCD_KEYFILE = cfg.etcd.keyFile; + FLANNELD_ETCD_CERTFILE = cfg.etcd.certFile; + FLANNELD_ETCD_CAFILE = cfg.etcd.caFile; + FLANNELD_IFACE = cfg.iface; + ETCDCTL_CERT_FILE = cfg.etcd.certFile; + ETCDCTL_KEY_FILE = cfg.etcd.keyFile; + ETCDCTL_CA_FILE = cfg.etcd.caFile; + ETCDCTL_PEERS = concatStringsSep "," cfg.etcd.endpoints; + }; + preStart = '' + echo "setting network configuration" + until ${pkgs.etcdctl.bin}/bin/etcdctl set /coreos.com/network/config '${builtins.toJSON networkConfig}' + do + echo "setting network configuration, retry" + sleep 1 + done + ''; + postStart = '' + while [ ! -f /run/flannel/subnet.env ] + do + sleep 1 + done + ''; + serviceConfig.ExecStart = "${cfg.package}/bin/flannel"; + }; + + services.etcd.enable = mkDefault cfg.etcd.endpoints == ["http://127.0.0.1:2379"]; + }; +} diff --git a/nixos/modules/services/networking/git-daemon.nix b/nixos/modules/services/networking/git-daemon.nix index 215ffe48a56..cd3fcd0f8f6 100644 --- a/nixos/modules/services/networking/git-daemon.nix +++ b/nixos/modules/services/networking/git-daemon.nix @@ -116,7 +116,8 @@ in }; systemd.services."git-daemon" = { - wantedBy = [ "ip-up.target" ]; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; script = "${pkgs.git}/bin/git daemon --reuseaddr " + (optionalString (cfg.basePath != "") "--base-path=${cfg.basePath} ") + (optionalString (cfg.listenAddress != "") "--listen=${cfg.listenAddress} ") diff --git a/nixos/modules/services/networking/gvpe.nix b/nixos/modules/services/networking/gvpe.nix index 27b64b5bb95..3ef3548e0a0 100644 --- a/nixos/modules/services/networking/gvpe.nix +++ b/nixos/modules/services/networking/gvpe.nix @@ -105,7 +105,7 @@ in config = mkIf cfg.enable { systemd.services.gvpe = { description = "GNU Virtual Private Ethernet node"; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; preStart = '' diff --git a/nixos/modules/services/networking/hostapd.nix b/nixos/modules/services/networking/hostapd.nix index 287964aab07..fd4545e88e2 100644 --- a/nixos/modules/services/networking/hostapd.nix +++ b/nixos/modules/services/networking/hostapd.nix @@ -86,7 +86,7 @@ in hwMode = mkOption { default = "g"; - type = types.string; + type = types.enum [ "a" "b" "g" ]; description = '' Operation mode. (a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g). @@ -140,7 +140,7 @@ in ieee80211n=1 ht_capab=[HT40-][SHORT-GI-40][DSSS_CCK-40] ''; - type = types.string; + type = types.lines; description = "Extra configuration options to put in hostapd.conf."; }; }; @@ -152,9 +152,6 @@ in config = mkIf cfg.enable { assertions = [ - { assertion = (cfg.hwMode == "a" || cfg.hwMode == "b" || cfg.hwMode == "g"); - message = "hwMode must be a/b/g"; - } { assertion = (cfg.channel >= 1 && cfg.channel <= 13); message = "channel must be between 1 and 13"; }]; diff --git a/nixos/modules/services/networking/htpdate.nix b/nixos/modules/services/networking/htpdate.nix new file mode 100644 index 00000000000..f5d512c7cd5 --- /dev/null +++ b/nixos/modules/services/networking/htpdate.nix @@ -0,0 +1,80 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + inherit (pkgs) htpdate; + + cfg = config.services.htpdate; +in + +{ + + ###### interface + + options = { + + services.htpdate = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enable htpdate daemon. + ''; + }; + + extraOptions = mkOption { + type = types.str; + default = ""; + description = '' + Additional command line arguments to pass to htpdate. + ''; + }; + + servers = mkOption { + type = types.listOf types.str; + default = [ "www.google.com" ]; + description = '' + HTTP servers to use for time synchronization. + ''; + }; + + proxy = mkOption { + type = types.str; + default = ""; + example = "127.0.0.1:8118"; + description = '' + HTTP proxy used for requests. + ''; + }; + + }; + + }; + + ###### implementation + + config = mkIf cfg.enable { + + systemd.services.htpdate = { + description = "htpdate daemon"; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "forking"; + PIDFile = "/var/run/htpdate.pid"; + ExecStart = concatStringsSep " " [ + "${htpdate}/bin/htpdate" + "-D -u nobody" + "-a -s" + "-l" + "${optionalString (cfg.proxy != "") "-P ${cfg.proxy}"}" + "${cfg.extraOptions}" + "${concatStringsSep " " cfg.servers}" + ]; + }; + }; + + }; + +} diff --git a/nixos/modules/services/networking/i2pd.nix b/nixos/modules/services/networking/i2pd.nix index 0cbf57314c4..abb7a4e9137 100644 --- a/nixos/modules/services/networking/i2pd.nix +++ b/nixos/modules/services/networking/i2pd.nix @@ -10,7 +10,7 @@ let extip = "EXTIP=\$(${pkgs.curl.bin}/bin/curl -sf \"http://jsonip.com\" | ${pkgs.gawk}/bin/awk -F'\"' '{print $4}')"; - toYesNo = b: if b then "yes" else "no"; + toYesNo = b: if b then "true" else "false"; mkEndpointOpt = name: addr: port: { enable = mkEnableOption name; @@ -31,6 +31,17 @@ let }; }; + mkKeyedEndpointOpt = name: addr: port: keyFile: + (mkEndpointOpt name addr port) // { + keys = mkOption { + type = types.str; + default = ""; + description = '' + File to persist ${lib.toUpper name} keys. + ''; + }; + }; + commonTunOpts = let i2cpOpts = { length = mkOption { @@ -63,19 +74,49 @@ let }; } // mkEndpointOpt name "127.0.0.1" 0; - i2pdConf = pkgs.writeText "i2pd.conf" '' - ipv6 = ${toYesNo cfg.enableIPv6} - notransit = ${toYesNo cfg.notransit} - floodfill = ${toYesNo cfg.floodfill} - ${if isNull cfg.port then "" else "port = ${toString cfg.port}"} - ${flip concatMapStrings - (collect (proto: proto ? port && proto ? address && proto ? name) cfg.proto) - (proto: let portStr = toString proto.port; in '' - [${proto.name}] - address = ${proto.address} - port = ${toString proto.port} - enabled = ${toYesNo proto.enable} - '') + i2pdConf = pkgs.writeText "i2pd.conf" + '' + ipv4 = ${toYesNo cfg.enableIPv4} + ipv6 = ${toYesNo cfg.enableIPv6} + notransit = ${toYesNo cfg.notransit} + floodfill = ${toYesNo cfg.floodfill} + netid = ${toString cfg.netid} + ${if isNull cfg.bandwidth then "" else "bandwidth = ${toString cfg.bandwidth}" } + ${if isNull cfg.port then "" else "port = ${toString cfg.port}"} + + [limits] + transittunnels = ${toString cfg.limits.transittunnels} + + [upnp] + enabled = ${toYesNo cfg.upnp.enable} + name = ${cfg.upnp.name} + + [precomputation] + elgamal = ${toYesNo cfg.precomputation.elgamal} + + [reseed] + verify = ${toYesNo cfg.reseed.verify} + file = ${cfg.reseed.file} + urls = ${builtins.concatStringsSep "," cfg.reseed.urls} + + [addressbook] + defaulturl = ${cfg.addressbook.defaulturl} + subscriptions = ${builtins.concatStringsSep "," cfg.addressbook.subscriptions} + ${flip concatMapStrings + (collect (proto: proto ? port && proto ? address && proto ? name) cfg.proto) + (proto: let portStr = toString proto.port; in + '' + [${proto.name}] + enabled = ${toYesNo proto.enable} + address = ${proto.address} + port = ${toString proto.port} + ${if proto ? keys then "keys = ${proto.keys}" else ""} + ${if proto ? auth then "auth = ${toYesNo proto.auth}" else ""} + ${if proto ? user then "user = ${proto.user}" else ""} + ${if proto ? pass then "pass = ${proto.pass}" else ""} + ${if proto ? outproxy then "outproxy = ${proto.outproxy}" else ""} + ${if proto ? outproxyPort then "outproxyport = ${toString proto.outproxyPort}" else ""} + '') } ''; @@ -106,7 +147,7 @@ let host = ${tun.address} port = ${tun.port} inport = ${tun.inPort} - accesslist = ${concatStringSep "," tun.accessList} + accesslist = ${builtins.concatStringsSep "," tun.accessList} '') } ''; @@ -114,7 +155,7 @@ let i2pdSh = pkgs.writeScriptBin "i2pd" '' #!/bin/sh ${if isNull cfg.extIp then extip else ""} - ${pkgs.i2pd}/bin/i2pd --log=1 \ + ${pkgs.i2pd}/bin/i2pd \ --host=${if isNull cfg.extIp then "$EXTIP" else cfg.extIp} \ --conf=${i2pdConf} \ --tunconf=${i2pdTunnelConf} @@ -135,6 +176,8 @@ in default = false; description = '' Enables I2Pd as a running service upon activation. + Please read http://i2pd.readthedocs.io/en/latest/ for further + configuration help. ''; }; @@ -162,6 +205,22 @@ in ''; }; + netid = mkOption { + type = types.int; + default = 2; + description = '' + I2P overlay netid. + ''; + }; + + bandwidth = mkOption { + type = with types; nullOr int; + default = null; + description = '' + Set a router bandwidth limit integer in kbps or letters: L (32), O (256), P (2048), X (>9000) + ''; + }; + port = mkOption { type = with types; nullOr int; default = null; @@ -170,6 +229,14 @@ in ''; }; + enableIPv4 = mkOption { + type = types.bool; + default = true; + description = '' + Enables IPv4 connectivity. Enabled by default. + ''; + }; + enableIPv6 = mkOption { type = types.bool; default = false; @@ -178,53 +245,177 @@ in ''; }; - proto.http = mkEndpointOpt "http" "127.0.0.1" 7070; + upnp = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enables UPnP. + ''; + }; + + name = mkOption { + type = types.str; + default = "I2Pd"; + description = '' + Name i2pd appears in UPnP forwardings list. + ''; + }; + }; + + precomputation.elgamal = mkOption { + type = types.bool; + default = false; + description = '' + Use ElGamal precomputated tables. + ''; + }; + + reseed = { + verify = mkOption { + type = types.bool; + default = false; + description = '' + Request SU3 signature verification + ''; + }; + + file = mkOption { + type = types.str; + default = ""; + description = '' + Full path to SU3 file to reseed from + ''; + }; + + urls = mkOption { + type = with types; listOf str; + default = [ + "https://reseed.i2p-project.de/" + "https://i2p.mooo.com/netDb/" + "https://netdb.i2p2.no/" + "https://us.reseed.i2p2.no:444/" + "https://uk.reseed.i2p2.no:444/" + "https://i2p.manas.ca:8443/" + ]; + description = '' + Reseed URLs + ''; + }; + }; + + addressbook = { + defaulturl = mkOption { + type = types.str; + default = "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt"; + description = '' + AddressBook subscription URL for initial setup + ''; + }; + subscriptions = mkOption { + type = with types; listOf str; + default = [ + "http://inr.i2p/export/alive-hosts.txt" + "http://i2p-projekt.i2p/hosts.txt" + "http://stats.i2p/cgi-bin/newhosts.txt" + ]; + description = '' + AddressBook subscription URLs + ''; + }; + }; + + limits.transittunnels = mkOption { + type = types.int; + default = 2500; + description = '' + Maximum number of active transit sessions + ''; + }; + + proto.http = (mkEndpointOpt "http" "127.0.0.1" 7070) // { + auth = mkOption { + type = types.bool; + default = false; + description = '' + Enable authentication for webconsole. + ''; + }; + user = mkOption { + type = types.str; + default = "i2pd"; + description = '' + Username for webconsole access + ''; + }; + pass = mkOption { + type = types.str; + default = "i2pd"; + description = '' + Password for webconsole access. + ''; + }; + }; + + proto.httpProxy = mkKeyedEndpointOpt "httpproxy" "127.0.0.1" 4446 ""; + proto.socksProxy = (mkKeyedEndpointOpt "socksproxy" "127.0.0.1" 4447 "") + // { + outproxy = mkOption { + type = types.str; + default = "127.0.0.1"; + description = "Upstream outproxy bind address."; + }; + outproxyPort = mkOption { + type = types.int; + default = 4444; + description = "Upstream outproxy bind port."; + }; + }; + proto.sam = mkEndpointOpt "sam" "127.0.0.1" 7656; proto.bob = mkEndpointOpt "bob" "127.0.0.1" 2827; + proto.i2cp = mkEndpointOpt "i2cp" "127.0.0.1" 7654; proto.i2pControl = mkEndpointOpt "i2pcontrol" "127.0.0.1" 7650; - proto.httpProxy = mkEndpointOpt "httpproxy" "127.0.0.1" 4446; - proto.socksProxy = mkEndpointOpt "socksproxy" "127.0.0.1" 4447; outTunnels = mkOption { default = {}; - type = with types; loaOf optionSet; + type = with types; loaOf (submodule ( + { name, config, ... }: { + options = commonTunOpts name; + config = { + name = mkDefault name; + }; + } + )); description = '' Connect to someone as a client and establish a local accept endpoint ''; - options = [ ({ name, config, ... }: { - options = commonTunOpts name; - config = { - name = mkDefault name; - }; - }) ]; }; inTunnels = mkOption { default = {}; - type = with types; loaOf optionSet; + type = with types; loaOf (submodule ( + { name, config, ... }: { + options = { + inPort = mkOption { + type = types.int; + default = 0; + description = "Service port. Default to the tunnel's listen port."; + }; + accessList = mkOption { + type = with types; listOf str; + default = []; + description = "I2P nodes that are allowed to connect to this service."; + }; + } // commonTunOpts name; + config = { + name = mkDefault name; + }; + } + )); description = '' Serve something on I2P network at port and delegate requests to address inPort. ''; - options = [ ({ name, config, ... }: { - - options = { - inPort = mkOption { - type = types.int; - default = 0; - description = "Service port. Default to the tunnel's listen port."; - }; - accessList = mkOption { - type = with types; listOf str; - default = []; - description = "I2P nodes that are allowed to connect to this service."; - }; - } // commonTunOpts name; - - config = { - name = mkDefault name; - }; - - }) ]; }; }; }; diff --git a/nixos/modules/services/networking/iodine.nix b/nixos/modules/services/networking/iodine.nix index 1b0d2d9a517..512dbd77ae4 100644 --- a/nixos/modules/services/networking/iodine.nix +++ b/nixos/modules/services/networking/iodine.nix @@ -106,7 +106,8 @@ in createIodineClientService = name: cfg: { description = "iodine client - ${name}"; - wantedBy = [ "ip-up.target" ]; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; serviceConfig = { RestartSec = "30s"; Restart = "always"; @@ -121,7 +122,8 @@ in ) // { iodined = mkIf (cfg.server.enable) { description = "iodine, ip over dns server daemon"; - wantedBy = [ "ip-up.target" ]; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; serviceConfig.ExecStart = "${pkgs.iodine}/bin/iodined -f -u ${iodinedUser} ${cfg.server.extraConfig} ${cfg.server.ip} ${cfg.server.domain}"; }; }; diff --git a/nixos/modules/services/networking/ircd-hybrid/builder.sh b/nixos/modules/services/networking/ircd-hybrid/builder.sh index f2c92878a4d..38312210df2 100644 --- a/nixos/modules/services/networking/ircd-hybrid/builder.sh +++ b/nixos/modules/services/networking/ircd-hybrid/builder.sh @@ -12,7 +12,7 @@ for i in $scripts; do if test "$(echo $i | cut -c1-2)" = "=>"; then subDir=$(echo $i | cut -c3-) else - dst=$out/$subDir/$((stripHash $i; echo $strippedName) | sed 's/\.in//') + dst=$out/$subDir/$(stripHash $i | sed 's/\.in//') doSub $i $dst chmod +x $dst # !!! fi @@ -23,7 +23,7 @@ for i in $substFiles; do if test "$(echo $i | cut -c1-2)" = "=>"; then subDir=$(echo $i | cut -c3-) else - dst=$out/$subDir/$((stripHash $i; echo $strippedName) | sed 's/\.in//') + dst=$out/$subDir/$(stripHash $i | sed 's/\.in//') doSub $i $dst fi done diff --git a/nixos/modules/services/networking/kippo.nix b/nixos/modules/services/networking/kippo.nix index 5f3efcd133a..834de4fdc09 100644 --- a/nixos/modules/services/networking/kippo.nix +++ b/nixos/modules/services/networking/kippo.nix @@ -46,7 +46,7 @@ rec { }; extraConfig = mkOption { default = ""; - type = types.string; + type = types.lines; description = ''Extra verbatim configuration added to the end of kippo.cfg.''; }; }; @@ -54,7 +54,7 @@ rec { }; config = mkIf cfg.enable { environment.systemPackages = with pkgs.pythonPackages; [ - python twisted_11 pycrypto pyasn1 ]; + python pkgs.kippo.twisted pycrypto pyasn1 ]; environment.etc."kippo.cfg".text = '' # Automatically generated by NixOS. @@ -84,7 +84,7 @@ rec { description = "Kippo Web Server"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - environment.PYTHONPATH = "${pkgs.kippo}/src/:${pkgs.pythonPackages.pycrypto}/lib/python2.7/site-packages/:${pkgs.pythonPackages.pyasn1}/lib/python2.7/site-packages/:${pkgs.pythonPackages.python}/lib/python2.7/site-packages/:${pkgs.pythonPackages.twisted_11}/lib/python2.7/site-packages/:."; + environment.PYTHONPATH = "${pkgs.kippo}/src/:${pkgs.pythonPackages.pycrypto}/lib/python2.7/site-packages/:${pkgs.pythonPackages.pyasn1}/lib/python2.7/site-packages/:${pkgs.pythonPackages.python}/lib/python2.7/site-packages/:${pkgs.kippo.twisted}/lib/python2.7/site-packages/:."; preStart = '' if [ ! -d ${cfg.varPath}/ ] ; then mkdir -p ${cfg.logPath}/tty @@ -107,7 +107,7 @@ rec { fi ''; - serviceConfig.ExecStart = "${pkgs.pythonPackages.twisted_11}/bin/twistd -y ${pkgs.kippo}/src/kippo.tac --syslog --rundir=${cfg.varPath}/ --pidfile=${cfg.pidPath}/kippo.pid --prefix=kippo -n"; + serviceConfig.ExecStart = "${pkgs.kippo.twisted}/bin/twistd -y ${pkgs.kippo}/src/kippo.tac --syslog --rundir=${cfg.varPath}/ --pidfile=${cfg.pidPath}/kippo.pid --prefix=kippo -n"; serviceConfig.PermissionsStartOnly = true; serviceConfig.User = "kippo"; serviceConfig.Group = "kippo"; diff --git a/nixos/modules/services/networking/miredo.nix b/nixos/modules/services/networking/miredo.nix new file mode 100644 index 00000000000..932d6cf2903 --- /dev/null +++ b/nixos/modules/services/networking/miredo.nix @@ -0,0 +1,93 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.miredo; + pidFile = "/run/miredo.pid"; + miredoConf = pkgs.writeText "miredo.conf" '' + InterfaceName ${cfg.interfaceName} + ServerAddress ${cfg.serverAddress} + ${optionalString (cfg.bindAddress != null) "BindAddress ${cfg.bindAddress}"} + ${optionalString (cfg.bindPort != null) "BindPort ${cfg.bindPort}"} + ''; +in +{ + + ###### interface + + options = { + + services.miredo = { + + enable = mkEnableOption "Whether miredo should be run on startup."; + + package = mkOption { + type = types.package; + default = pkgs.miredo; + defaultText = "pkgs.miredo"; + description = '' + The package to use for the miredo daemon's binary. + ''; + }; + + serverAddress = mkOption { + default = "teredo.remlab.net"; + type = types.str; + description = '' + The hostname or primary IPv4 address of the Teredo server. + This setting is required if Miredo runs as a Teredo client. + "teredo.remlab.net" is an experimental service for testing only. + Please use another server for production and/or large scale deployments. + ''; + }; + + interfaceName = mkOption { + default = "teredo"; + type = types.str; + description = '' + Name of the network tunneling interface. + ''; + }; + + bindAddress = mkOption { + default = null; + type = types.nullOr types.str; + description = '' + Depending on the local firewall/NAT rules, you might need to force + Miredo to use a fixed UDP port and or IPv4 address. + ''; + }; + + bindPort = mkOption { + default = null; + type = types.nullOr types.str; + description = '' + Depending on the local firewall/NAT rules, you might need to force + Miredo to use a fixed UDP port and or IPv4 address. + ''; + }; + }; + }; + + + ###### implementation + + config = mkIf cfg.enable { + + systemd.services.miredo = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + description = "Teredo IPv6 Tunneling Daemon"; + serviceConfig = { + Restart = "always"; + RestartSec = "5s"; + ExecStartPre = "${cfg.package}/bin/miredo-checkconf -f ${miredoConf}"; + ExecStart = "${cfg.package}/bin/miredo -c ${miredoConf} -p ${pidFile} -f"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + }; + }; + + }; + +} diff --git a/nixos/modules/services/networking/mjpg-streamer.nix b/nixos/modules/services/networking/mjpg-streamer.nix index 9986f549aec..1286b0c7ef6 100644 --- a/nixos/modules/services/networking/mjpg-streamer.nix +++ b/nixos/modules/services/networking/mjpg-streamer.nix @@ -59,8 +59,12 @@ in { description = "mjpg-streamer webcam streamer"; wantedBy = [ "multi-user.target" ]; - serviceConfig.User = cfg.user; - serviceConfig.Group = cfg.group; + serviceConfig = { + User = cfg.user; + Group = cfg.group; + Restart = "on-failure"; + RestartSec = 1; + }; script = '' IPLUGIN="${cfg.inputPlugin}" diff --git a/nixos/modules/services/networking/murmur.nix b/nixos/modules/services/networking/murmur.nix index 1cc19a2c9e0..81f968ae9fe 100644 --- a/nixos/modules/services/networking/murmur.nix +++ b/nixos/modules/services/networking/murmur.nix @@ -15,7 +15,7 @@ let logfile=/var/log/murmur/murmurd.log pidfile=${cfg.pidfile} - welcome="${cfg.welcome}" + welcometext="${cfg.welcometext}" port=${toString cfg.port} ${if cfg.hostName == "" then "" else "host="+cfg.hostName} @@ -84,7 +84,7 @@ in description = "Path to PID file for Murmur daemon."; }; - welcome = mkOption { + welcometext = mkOption { type = types.str; default = ""; description = "Welcome message for connected clients."; @@ -230,7 +230,7 @@ in }; extraConfig = mkOption { - type = types.str; + type = types.lines; default = ""; description = "Extra configuration to put into mumur.ini."; }; diff --git a/nixos/modules/services/networking/nat.nix b/nixos/modules/services/networking/nat.nix index 9d163e60d5e..08ba2fdb164 100644 --- a/nixos/modules/services/networking/nat.nix +++ b/nixos/modules/services/networking/nat.nix @@ -122,23 +122,23 @@ in }; networking.nat.forwardPorts = mkOption { - type = types.listOf types.optionSet; + type = with types; listOf (submodule { + options = { + sourcePort = mkOption { + type = types.int; + example = 8080; + description = "Source port of the external interface"; + }; + + destination = mkOption { + type = types.str; + example = "10.0.0.1:80"; + description = "Forward tcp connection to destination ip:port"; + }; + }; + }); default = []; example = [ { sourcePort = 8080; destination = "10.0.0.1:80"; } ]; - options = { - sourcePort = mkOption { - type = types.int; - example = 8080; - description = "Source port of the external interface"; - }; - - destination = mkOption { - type = types.str; - example = "10.0.0.1:80"; - description = "Forward tcp connection to destination ip:port"; - }; - }; - description = '' List of forwarded ports from the external interface to @@ -171,7 +171,7 @@ in systemd.services = mkIf (!config.networking.firewall.enable) { nat = { description = "Network Address Translation"; wantedBy = [ "network.target" ]; - after = [ "network-interfaces.target" "systemd-modules-load.service" ]; + after = [ "network-pre.target" "systemd-modules-load.service" ]; path = [ pkgs.iptables ]; unitConfig.ConditionCapability = "CAP_NET_ADMIN"; diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix index d198e3bfc02..8f353979d3f 100644 --- a/nixos/modules/services/networking/networkmanager.nix +++ b/nixos/modules/services/networking/networkmanager.nix @@ -52,14 +52,6 @@ let }); ''; - ipUpScript = writeScript "01nixos-ip-up" '' - #!/bin/sh - if test "$2" = "up"; then - ${config.systemd.package}/bin/systemctl start ip-up.target - ${config.systemd.package}/bin/systemctl start network-online.target - fi - ''; - ns = xs: writeText "nameservers" ( concatStrings (map (s: "nameserver ${s}\n") xs) ); @@ -188,9 +180,6 @@ in { boot.kernelModules = [ "ppp_mppe" ]; # Needed for most (all?) PPTP VPN connections. environment.etc = with cfg.basePackages; [ - { source = ipUpScript; - target = "NetworkManager/dispatcher.d/01nixos-ip-up"; - } { source = configFile; target = "NetworkManager/NetworkManager.conf"; } @@ -209,6 +198,9 @@ in { { source = "${networkmanager_l2tp}/etc/NetworkManager/VPN/nm-l2tp-service.name"; target = "NetworkManager/VPN/nm-l2tp-service.name"; } + { source = "${networkmanager_strongswan}/etc/NetworkManager/VPN/nm-strongswan-service.name"; + target = "NetworkManager/VPN/nm-strongswan-service.name"; + } ] ++ optional (cfg.appendNameservers == [] || cfg.insertNameservers == []) { source = overrideNameserversScript; target = "NetworkManager/dispatcher.d/02overridedns"; diff --git a/nixos/modules/services/networking/nntp-proxy.nix b/nixos/modules/services/networking/nntp-proxy.nix index dca8ccac762..7eebecb23b0 100644 --- a/nixos/modules/services/networking/nntp-proxy.nix +++ b/nixos/modules/services/networking/nntp-proxy.nix @@ -148,11 +148,11 @@ in }; verbosity = mkOption { - type = types.str; + type = types.enum [ "error" "warning" "notice" "info" "debug" ]; default = "info"; example = "error"; description = '' - Verbosity level (error, warning, notice, info, debug) + Verbosity level ''; }; diff --git a/nixos/modules/services/networking/nsd.nix b/nixos/modules/services/networking/nsd.nix index 333a3378c4c..481e267f6c3 100644 --- a/nixos/modules/services/networking/nsd.nix +++ b/nixos/modules/services/networking/nsd.nix @@ -71,6 +71,7 @@ let # interfaces ${forEach " ip-address: " cfg.interfaces} + ip-freebind: ${yesOrNo cfg.ipFreebind} hide-version: ${yesOrNo cfg.hideVersion} identity: "${cfg.identity}" ip-transparent: ${yesOrNo cfg.ipTransparent} @@ -84,7 +85,7 @@ let reuseport: ${yesOrNo cfg.reuseport} round-robin: ${yesOrNo cfg.roundRobin} server-count: ${toString cfg.serverCount} - ${if cfg.statistics == null then "" else "statistics: ${toString cfg.statistics}"} + ${maybeToString "statistics: " cfg.statistics} tcp-count: ${toString cfg.tcpCount} tcp-query-count: ${toString cfg.tcpQueryCount} tcp-timeout: ${toString cfg.tcpTimeout} @@ -117,7 +118,8 @@ let ''; yesOrNo = b: if b then "yes" else "no"; - maybeString = pre: s: if s == null then "" else ''${pre} "${s}"''; + maybeString = prefix: x: if x == null then "" else ''${prefix} "${x}"''; + maybeToString = prefix: x: if x == null then "" else ''${prefix} ${toString x}''; forEach = pre: l: concatMapStrings (x: pre + x + "\n") l; @@ -146,6 +148,11 @@ let ${forEach " rrl-whitelist: " zone.rrlWhitelist} ${maybeString "zonestats: " zone.zoneStats} + ${maybeToString "max-refresh-time: " zone.maxRefreshSecs} + ${maybeToString "min-refresh-time: " zone.minRefreshSecs} + ${maybeToString "max-retry-time: " zone.maxRetrySecs} + ${maybeToString "min-retry-time: " zone.minRetrySecs} + allow-axfr-fallback: ${yesOrNo zone.allowAXFRFallback} ${forEach " allow-notify: " zone.allowNotify} ${forEach " request-xfr: " zone.requestXFR} @@ -241,6 +248,44 @@ let ''; }; + maxRefreshSecs = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + Limit refresh time for secondary zones. This is the timer which + checks to see if the zone has to be refetched when it expires. + Normally the value from the SOA record is used, but this option + restricts that value. + ''; + }; + + minRefreshSecs = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + Limit refresh time for secondary zones. + ''; + }; + + maxRetrySecs = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + Limit retry time for secondary zones. This is the timeout after + a failed fetch attempt for the zone. Normally the value from + the SOA record is used, but this option restricts that value. + ''; + }; + + minRetrySecs = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + Limit retry time for secondary zones. + ''; + }; + + notify = mkOption { type = types.listOf types.str; default = []; @@ -300,12 +345,10 @@ let }; rrlWhitelist = mkOption { - type = types.listOf types.str; + type = with types; listOf (enum [ "nxdomain" "error" "referral" "any" "rrsig" "wildcard" "nodata" "dnskey" "positive" "all" ]); default = []; description = '' Whitelists the given rrl-types. - The RRL classification types are: nxdomain, error, referral, any, - rrsig, wildcard, nodata, dnskey, positive, all ''; }; @@ -366,6 +409,15 @@ in ''; }; + ipFreebind = mkOption { + type = types.bool; + default = false; + description = '' + Whether to bind to nonlocal addresses and interfaces that are down. + Similar to ip-transparent. + ''; + }; + ipTransparent = mkOption { type = types.bool; default = false; diff --git a/nixos/modules/services/networking/ntpd.nix b/nixos/modules/services/networking/ntpd.nix index c8a08567928..88e6dbf22b9 100644 --- a/nixos/modules/services/networking/ntpd.nix +++ b/nixos/modules/services/networking/ntpd.nix @@ -34,7 +34,7 @@ in services.ntp = { enable = mkOption { - default = !config.boot.isContainer; + default = false; description = '' Whether to synchronise your machine's time using the NTP protocol. @@ -42,12 +42,7 @@ in }; servers = mkOption { - default = [ - "0.nixos.pool.ntp.org" - "1.nixos.pool.ntp.org" - "2.nixos.pool.ntp.org" - "3.nixos.pool.ntp.org" - ]; + default = config.networking.timeServers; description = '' The set of NTP servers from which to synchronise. ''; @@ -70,6 +65,7 @@ in # Make tools such as ntpq available in the system path. environment.systemPackages = [ pkgs.ntp ]; + services.timesyncd.enable = mkForce false; users.extraUsers = singleton { name = ntpUser; diff --git a/nixos/modules/services/networking/oidentd.nix b/nixos/modules/services/networking/oidentd.nix index 651bb8e967c..ba7acd87954 100644 --- a/nixos/modules/services/networking/oidentd.nix +++ b/nixos/modules/services/networking/oidentd.nix @@ -25,7 +25,7 @@ with lib; config = mkIf config.services.oidentd.enable { systemd.services.oidentd = { - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig.Type = "forking"; script = "${pkgs.oidentd}/sbin/oidentd -u oidentd -g nogroup" + diff --git a/nixos/modules/services/networking/openfire.nix b/nixos/modules/services/networking/openfire.nix index ed91b45ec94..4059eb3db83 100644 --- a/nixos/modules/services/networking/openfire.nix +++ b/nixos/modules/services/networking/openfire.nix @@ -34,7 +34,7 @@ with lib; assertions = singleton { assertion = !(config.services.openfire.usePostgreSQL -> config.services.postgresql.enable); - message = "OpenFire assertion failed."; + message = "OpenFire configured to use PostgreSQL but services.postgresql.enable is not enabled."; }; systemd.services.openfire = { @@ -47,7 +47,7 @@ with lib; export HOME=/tmp mkdir /var/log/openfire || true mkdir /etc/openfire || true - for i in ${openfire}/conf.inst/*; do + for i in ${pkgs.openfire}/conf.inst/*; do if ! test -f /etc/openfire/$(basename $i); then cp $i /etc/openfire/ fi diff --git a/nixos/modules/services/networking/openntpd.nix b/nixos/modules/services/networking/openntpd.nix index a8625fa2fa9..13a1b5258ce 100644 --- a/nixos/modules/services/networking/openntpd.nix +++ b/nixos/modules/services/networking/openntpd.nix @@ -49,7 +49,7 @@ in ###### implementation config = mkIf cfg.enable { - services.ntp.enable = mkForce false; + services.timesyncd.enable = mkForce false; # Add ntpctl to the environment for status checking environment.systemPackages = [ package ]; diff --git a/nixos/modules/services/networking/openvpn.nix b/nixos/modules/services/networking/openvpn.nix index 82173a841a3..3fbf5a9f022 100644 --- a/nixos/modules/services/networking/openvpn.nix +++ b/nixos/modules/services/networking/openvpn.nix @@ -56,7 +56,7 @@ let description = "OpenVPN instance ‘${name}’"; wantedBy = optional cfg.autoStart "multi-user.target"; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; path = [ pkgs.iptables pkgs.iproute pkgs.nettools ]; @@ -116,52 +116,54 @@ in attribute name. ''; - type = types.attrsOf types.optionSet; + type = with types; attrsOf (submodule { - options = { + options = { - config = mkOption { - type = types.lines; - description = '' - Configuration of this OpenVPN instance. See - <citerefentry><refentrytitle>openvpn</refentrytitle><manvolnum>8</manvolnum></citerefentry> - for details. - ''; - }; + config = mkOption { + type = types.lines; + description = '' + Configuration of this OpenVPN instance. See + <citerefentry><refentrytitle>openvpn</refentrytitle><manvolnum>8</manvolnum></citerefentry> + for details. + ''; + }; - up = mkOption { - default = ""; - type = types.lines; - description = '' - Shell commands executed when the instance is starting. - ''; - }; + up = mkOption { + default = ""; + type = types.lines; + description = '' + Shell commands executed when the instance is starting. + ''; + }; - down = mkOption { - default = ""; - type = types.lines; - description = '' - Shell commands executed when the instance is shutting down. - ''; - }; + down = mkOption { + default = ""; + type = types.lines; + description = '' + Shell commands executed when the instance is shutting down. + ''; + }; - autoStart = mkOption { - default = true; - type = types.bool; - description = "Whether this OpenVPN instance should be started automatically."; - }; + autoStart = mkOption { + default = true; + type = types.bool; + description = "Whether this OpenVPN instance should be started automatically."; + }; + + updateResolvConf = mkOption { + default = false; + type = types.bool; + description = '' + Use the script from the update-resolv-conf package to automatically + update resolv.conf with the DNS information provided by openvpn. The + script will be run after the "up" commands and before the "down" commands. + ''; + }; - updateResolvConf = mkOption { - default = false; - type = types.bool; - description = '' - Use the script from the update-resolv-conf package to automatically - update resolv.conf with the DNS information provided by openvpn. The - script will be run after the "up" commands and before the "down" commands. - ''; }; - }; + }); }; diff --git a/nixos/modules/services/networking/powerdns.nix b/nixos/modules/services/networking/powerdns.nix new file mode 100644 index 00000000000..ba05e15389f --- /dev/null +++ b/nixos/modules/services/networking/powerdns.nix @@ -0,0 +1,49 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.powerdns; + configDir = pkgs.writeTextDir "pdns.conf" "${cfg.extraConfig}"; +in { + options = { + services.powerdns = { + enable = mkEnableOption "Powerdns domain name server"; + + extraConfig = mkOption { + type = types.lines; + default = "launch=bind"; + description = '' + Extra lines to be added verbatim to pdns.conf. + Powerdns will chroot to /var/lib/powerdns. + So any file, powerdns is supposed to be read, + should be in /var/lib/powerdns and needs to specified + relative to the chroot. + ''; + }; + }; + }; + + config = mkIf config.services.powerdns.enable { + systemd.services.pdns = { + unitConfig.Documentation = "man:pdns_server(1) man:pdns_control(1)"; + description = "Powerdns name server"; + wantedBy = [ "multi-user.target" ]; + after = ["network.target" "mysql.service" "postgresql.service" "openldap.service"]; + + serviceConfig = { + Restart="on-failure"; + RestartSec="1"; + StartLimitInterval="0"; + PrivateDevices=true; + CapabilityBoundingSet="CAP_CHOWN CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID CAP_SYS_CHROOT"; + NoNewPrivileges=true; + ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p /var/lib/powerdns"; + ExecStart = "${pkgs.powerdns}/bin/pdns_server --setuid=nobody --setgid=nogroup --chroot=/var/lib/powerdns --socket-dir=/ --daemon=no --guardian=no --disable-syslog --write-pid=no --config-dir=${configDir}"; + ProtectSystem="full"; + ProtectHome=true; + RestrictAddressFamilies="AF_UNIX AF_INET AF_INET6"; + }; + }; + }; +} diff --git a/nixos/modules/services/networking/prayer.nix b/nixos/modules/services/networking/prayer.nix index 4e1d66bc110..67d8cece611 100644 --- a/nixos/modules/services/networking/prayer.nix +++ b/nixos/modules/services/networking/prayer.nix @@ -56,6 +56,7 @@ in }; extraConfig = mkOption { + type = types.lines; default = "" ; description = '' Extra configuration. Contents will be added verbatim to the configuration file. diff --git a/nixos/modules/services/networking/privoxy.nix b/nixos/modules/services/networking/privoxy.nix index 94beb78ef5a..49ca839a2c3 100644 --- a/nixos/modules/services/networking/privoxy.nix +++ b/nixos/modules/services/networking/privoxy.nix @@ -6,8 +6,6 @@ let inherit (pkgs) privoxy; - privoxyUser = "privoxy"; - cfg = config.services.privoxy; confFile = pkgs.writeText "privoxy.conf" '' @@ -88,18 +86,25 @@ in ###### implementation config = mkIf cfg.enable { - - users.extraUsers = singleton - { name = privoxyUser; - uid = config.ids.uids.privoxy; - description = "Privoxy daemon user"; - }; + + users.users.privoxy = { + isSystemUser = true; + home = "/var/empty"; + group = "privoxy"; + }; + + users.groups.privoxy = {}; 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}"; + serviceConfig.ExecStart = "${privoxy}/bin/privoxy --no-daemon --user privoxy ${confFile}"; + + serviceConfig.PrivateDevices = true; + serviceConfig.PrivateTmp = true; + serviceConfig.ProtectHome = true; + serviceConfig.ProtectSystem = "full"; }; }; diff --git a/nixos/modules/services/networking/prosody.nix b/nixos/modules/services/networking/prosody.nix index f82f8bfddbb..5682b506344 100644 --- a/nixos/modules/services/networking/prosody.nix +++ b/nixos/modules/services/networking/prosody.nix @@ -164,7 +164,7 @@ in description = "Define the virtual hosts"; - type = types.loaOf types.optionSet; + type = with types; loaOf (submodule vHostOpts); example = { myhost = { @@ -180,7 +180,6 @@ in }; }; - options = [ vHostOpts ]; }; ssl = mkOption { @@ -196,6 +195,7 @@ in }; extraConfig = mkOption { + type = types.lines; default = ''''; description = "Additional prosody configuration"; }; diff --git a/nixos/modules/services/networking/quagga.nix b/nixos/modules/services/networking/quagga.nix new file mode 100644 index 00000000000..ac83da92063 --- /dev/null +++ b/nixos/modules/services/networking/quagga.nix @@ -0,0 +1,187 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.quagga; + + services = [ "babel" "bgp" "isis" "ospf6" "ospf" "pim" "rip" "ripng" ]; + allServices = services ++ [ "zebra" ]; + + isEnabled = service: cfg.${service}.enable; + + daemonName = service: if service == "zebra" then service else "${service}d"; + + configFile = service: + let + scfg = cfg.${service}; + in + if scfg.configFile != null then scfg.configFile + else pkgs.writeText "${daemonName service}.conf" + '' + ! Quagga ${daemonName service} configuration + ! + hostname ${config.networking.hostName} + log syslog + service password-encryption + ! + ${scfg.config} + ! + end + ''; + + serviceOptions = service: + { + enable = mkEnableOption "the Quagga ${toUpper service} routing protocol"; + + configFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/etc/quagga/${daemonName service}.conf"; + description = '' + Configuration file to use for Quagga ${daemonName service}. + By default the NixOS generated files are used. + ''; + }; + + config = mkOption { + type = types.lines; + default = ""; + example = + let + examples = { + rip = '' + router rip + network 10.0.0.0/8 + ''; + + ospf = '' + router ospf + network 10.0.0.0/8 area 0 + ''; + + bgp = '' + router bgp 65001 + neighbor 10.0.0.1 remote-as 65001 + ''; + }; + in + examples.${service} or ""; + description = '' + ${daemonName service} configuration statements. + ''; + }; + + vtyListenAddress = mkOption { + type = types.str; + default = "127.0.0.1"; + description = '' + Address to bind to for the VTY interface. + ''; + }; + + vtyListenPort = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + TCP Port to bind to for the VTY interface. + ''; + }; + }; + +in + +{ + + ###### interface + + options.services.quagga = + { + + zebra = (serviceOptions "zebra") // { + + enable = mkOption { + type = types.bool; + default = any isEnabled services; + example = true; + description = '' + Whether to enable the Zebra routing manager. + + The Zebra routing manager is automatically enabled + if any routing protocols are configured. + ''; + }; + + }; + + } // (genAttrs services serviceOptions); + + ###### implementation + + config = mkIf (any isEnabled allServices) { + + environment.systemPackages = [ + pkgs.quagga # for the vtysh tool + ]; + + users.users.quagga = { + description = "Quagga daemon user"; + isSystemUser = true; + group = "quagga"; + }; + + users.groups = { + quagga = {}; + # Members of the quaggavty group can use vtysh to inspect the Quagga daemons + quaggavty = {}; + }; + + systemd.services = + let + quaggaService = service: + let + scfg = cfg.${service}; + daemon = daemonName service; + in + nameValuePair daemon ({ + wantedBy = [ "multi-user.target" ]; + restartTriggers = [ (configFile service) ]; + + serviceConfig = { + Type = "forking"; + PIDFile = "/run/quagga/${daemon}.pid"; + ExecStart = "@${pkgs.quagga}/libexec/quagga/${daemon} ${daemon} -d -f ${configFile service}" + + optionalString (scfg.vtyListenAddress != "") " -A ${scfg.vtyListenAddress}" + + optionalString (scfg.vtyListenPort != null) " -P ${toString scfg.vtyListenPort}"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + Restart = "on-abort"; + }; + } // ( + if service == "zebra" then + { + description = "Quagga Zebra routing manager"; + unitConfig.Documentation = "man:zebra(8)"; + after = [ "network.target" ]; + preStart = '' + install -m 0755 -o quagga -g quagga -d /run/quagga + + ${pkgs.iproute}/bin/ip route flush proto zebra + ''; + } + else + { + description = "Quagga ${toUpper service} routing daemon"; + unitConfig.Documentation = "man:${daemon}(8) man:zebra(8)"; + bindsTo = [ "zebra.service" ]; + after = [ "network.target" "zebra.service" ]; + } + )); + in + listToAttrs (map quaggaService (filter isEnabled allServices)); + + }; + + meta.maintainers = with lib.maintainers; [ tavyc ]; + +} diff --git a/nixos/modules/services/networking/quassel.nix b/nixos/modules/services/networking/quassel.nix index 99269c49e8f..edcc12170b2 100644 --- a/nixos/modules/services/networking/quassel.nix +++ b/nixos/modules/services/networking/quassel.nix @@ -3,8 +3,8 @@ with lib; let - quassel = pkgs.kde4.quasselDaemon; cfg = config.services.quassel; + quassel = cfg.package; user = if cfg.user != null then cfg.user else "quassel"; in @@ -23,6 +23,16 @@ in ''; }; + package = mkOption { + type = types.package; + default = pkgs.kde4.quasselDaemon; + defaultText = "pkgs.kde4.quasselDaemon"; + description = '' + The package of the quassel daemon. + ''; + example = literalExample "pkgs.quasselDaemon"; + }; + interfaces = mkOption { default = [ "127.0.0.1" ]; description = '' diff --git a/nixos/modules/services/networking/radicale.nix b/nixos/modules/services/networking/radicale.nix index 19762f4e570..f9300fdabc5 100644 --- a/nixos/modules/services/networking/radicale.nix +++ b/nixos/modules/services/networking/radicale.nix @@ -33,7 +33,7 @@ in }; config = mkIf cfg.enable { - environment.systemPackages = [ pkgs.pythonPackages.radicale ]; + environment.systemPackages = [ pkgs.radicale ]; users.extraUsers = singleton { name = "radicale"; @@ -50,10 +50,9 @@ in systemd.services.radicale = { description = "A Simple Calendar and Contact Server"; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - script = "${pkgs.pythonPackages.radicale}/bin/radicale -C ${confFile} -d"; - serviceConfig.Type = "forking"; + script = "${pkgs.radicale}/bin/radicale -C ${confFile} -f"; serviceConfig.User = "radicale"; serviceConfig.Group = "radicale"; }; diff --git a/nixos/modules/services/networking/skydns.nix b/nixos/modules/services/networking/skydns.nix index ba913482e3c..6ad18bb2240 100644 --- a/nixos/modules/services/networking/skydns.nix +++ b/nixos/modules/services/networking/skydns.nix @@ -11,7 +11,7 @@ in { etcd = { machines = mkOption { - default = [ "http://localhost:4001" ]; + default = [ "http://127.0.0.1:2379" ]; type = types.listOf types.str; description = "Skydns list of etcd endpoints to connect to."; }; diff --git a/nixos/modules/services/networking/smokeping.nix b/nixos/modules/services/networking/smokeping.nix index f7a5926dc64..04312c39062 100644 --- a/nixos/modules/services/networking/smokeping.nix +++ b/nixos/modules/services/networking/smokeping.nix @@ -6,31 +6,39 @@ let cfg = config.services.smokeping; smokepingHome = "/var/lib/smokeping"; smokepingPidDir = "/run"; - configFile = '' - *** General *** - owner = ${cfg.owner} - contact = ${cfg.ownerEmail} - mailhost = ${cfg.mailHost} - #sendmail = /var/setuid-wrappers/sendmail - imgcache = ${smokepingHome}/cache - imgurl = http://${cfg.hostName}:${builtins.toString cfg.port}/cache - datadir = ${smokepingHome}/data - piddir = ${smokepingPidDir} - cgiurl = http://${cfg.hostName}:${builtins.toString cfg.port}/smokeping.cgi - smokemail = ${cfg.smokeMailTemplate} - *** Presentation *** - template = ${cfg.presentationTemplate} - ${cfg.presentationConfig} - #*** Alerts *** - #${cfg.alertConfig} - *** Database *** - ${cfg.databaseConfig} - *** Probes *** - ${cfg.probeConfig} - *** Targets *** - ${cfg.targetConfig} - ${cfg.extraConfig} - ''; + configFile = + if cfg.config == null + then + '' + *** General *** + cgiurl = ${cfg.cgiUrl} + contact = ${cfg.ownerEmail} + datadir = ${smokepingHome}/data + imgcache = ${smokepingHome}/cache + imgurl = ${cfg.imgUrl} + linkstyle = ${cfg.linkStyle} + ${lib.optionalString (cfg.mailHost != "") "mailhost = ${cfg.mailHost}"} + owner = ${cfg.owner} + pagedir = ${smokepingHome}/cache + piddir = ${smokepingPidDir} + ${lib.optionalString (cfg.sendmail != null) "sendmail = ${cfg.sendmail}"} + smokemail = ${cfg.smokeMailTemplate} + *** Presentation *** + template = ${cfg.presentationTemplate} + ${cfg.presentationConfig} + *** Alerts *** + ${cfg.alertConfig} + *** Database *** + ${cfg.databaseConfig} + *** Probes *** + ${cfg.probeConfig} + *** Targets *** + ${cfg.targetConfig} + ${cfg.extraConfig} + '' + else + cfg.config; + configPath = pkgs.writeText "smokeping.conf" configFile; cgiHome = pkgs.writeScript "smokeping.fcgi" '' #!${pkgs.bash}/bin/bash @@ -46,58 +54,36 @@ in default = false; description = "Enable the smokeping service"; }; - webService = mkOption { - type = types.bool; - default = true; - description = "Enable a smokeping web interface"; - }; - - user = mkOption { - type = types.string; - default = "smokeping"; - description = "User that runs smokeping and (optionally) thttpd"; - }; - mailHost = mkOption { - type = types.string; - default = "127.0.0.1"; - description = "Use this SMTP server rather than localhost"; - }; - smokeMailTemplate = mkOption { + alertConfig = mkOption { type = types.string; - default = "${cfg.package}/etc/smokemail.dist"; - description = "Specify the smokemail template for alerts."; - }; + default = '' + to = root@localhost + from = smokeping@localhost + ''; + example = literalExample '' + to = alertee@address.somewhere + from = smokealert@company.xy - package = mkOption { - type = types.package; - default = pkgs.smokeping; - description = "Specify a custom smokeping package"; - }; - owner = mkOption { - type = types.string; - default = "nobody"; - example = "Joe Admin"; - description = "Real name of the owner of the instance"; + +someloss + type = loss + # in percent + pattern = >0%,*12*,>0%,*12*,>0% + comment = loss 3 times in a row; + ''; + description = "Configuration for alerts."; }; - hostName = mkOption { + cgiUrl = mkOption { type = types.string; - default = config.networking.hostName; - example = "somewhere.example.com"; - description = "DNS name for the urls generated in the cgi."; - }; - port = mkOption { - type = types.int; - default = 8081; - example = 8081; - description = "TCP port to use for the web server."; + default = "http://${cfg.hostName}:${builtins.toString cfg.port}/smokeping.cgi"; + example = "https://somewhere.example.com/smokeping.cgi"; + description = "URL to the smokeping cgi."; }; - ownerEmail = mkOption { - type = types.string; - default = "no-reply@${cfg.hostName}"; - example = "no-reply@yourdomain.com"; - description = "Email contact for owner"; + config = mkOption { + type = types.nullOr types.string; + default = null; + description = "Full smokeping config supplied by the user. Overrides " + + "and replaces any other configuration supplied."; }; - databaseConfig = mkOption { type = types.string; default = '' @@ -130,27 +116,59 @@ in Once set, changing the interval will require deletion or migration of all the collected data.''; }; - alertConfig = mkOption { + extraConfig = mkOption { + type = types.lines; + default = ""; + description = "Any additional customization not already included."; + }; + hostName = mkOption { + type = types.string; + default = config.networking.hostName; + example = "somewhere.example.com"; + description = "DNS name for the urls generated in the cgi."; + }; + imgUrl = mkOption { + type = types.string; + default = "http://${cfg.hostName}:${builtins.toString cfg.port}/cache"; + example = "https://somewhere.example.com/cache"; + description = "Base url for images generated in the cgi."; + }; + linkStyle = mkOption { + type = types.enum ["original" "absolute" "relative"]; + default = "relative"; + example = "absolute"; + description = "DNS name for the urls generated in the cgi."; + }; + mailHost = mkOption { type = types.string; default = ""; - example = literalExample '' - to = alertee@address.somewhere - from = smokealert@company.xy - - +someloss - type = loss - # in percent - pattern = >0%,*12*,>0%,*12*,>0% - comment = loss 3 times in a row; - ''; - description = "Configuration for alerts."; + example = "localhost"; + description = "Use this SMTP server to send alerts"; }; - presentationTemplate = mkOption { + owner = mkOption { type = types.string; - default = "${pkgs.smokeping}/etc/basepage.html.dist"; - description = "Default page layout for the web UI."; + default = "nobody"; + example = "Joe Admin"; + description = "Real name of the owner of the instance"; + }; + ownerEmail = mkOption { + type = types.string; + default = "no-reply@${cfg.hostName}"; + example = "no-reply@yourdomain.com"; + description = "Email contact for owner"; + }; + package = mkOption { + type = types.package; + default = pkgs.smokeping; + defaultText = "pkgs.smokeping"; + description = "Specify a custom smokeping package"; + }; + port = mkOption { + type = types.int; + default = 8081; + example = 8081; + description = "TCP port to use for the web server."; }; - presentationConfig = mkOption { type = types.string; default = '' @@ -192,14 +210,30 @@ in ''; description = "presentation graph style"; }; + presentationTemplate = mkOption { + type = types.string; + default = "${pkgs.smokeping}/etc/basepage.html.dist"; + description = "Default page layout for the web UI."; + }; probeConfig = mkOption { type = types.string; default = '' + FPing - binary = ${pkgs.fping}/bin/fping + binary = ${config.security.wrapperDir}/fping ''; description = "Probe configuration"; }; + sendmail = mkOption { + type = types.nullOr types.path; + default = null; + example = "/var/setuid-wrappers/sendmail"; + description = "Use this sendmail compatible script to deliver alerts"; + }; + smokeMailTemplate = mkOption { + type = types.string; + default = "${cfg.package}/etc/smokemail.dist"; + description = "Specify the smokemail template for alerts."; + }; targetConfig = mkOption { type = types.string; default = '' @@ -218,17 +252,29 @@ in ''; description = "Target configuration"; }; - extraConfig = mkOption { + user = mkOption { type = types.string; - default = ""; - description = "Any additional customization not already included."; + default = "smokeping"; + description = "User that runs smokeping and (optionally) thttpd"; + }; + webService = mkOption { + type = types.bool; + default = true; + description = "Enable a smokeping web interface"; }; - }; }; config = mkIf cfg.enable { + assertions = [ + { + assertion = !(cfg.sendmail != null && cfg.mailHost != ""); + message = "services.smokeping: sendmail and Mailhost cannot both be enabled."; + } + ]; + security.setuidPrograms = [ "fping" ]; + environment.systemPackages = [ pkgs.fping ]; users.extraUsers = singleton { name = cfg.user; isNormalUser = false; @@ -243,9 +289,12 @@ in serviceConfig.PermissionsStartOnly = true; preStart = '' mkdir -m 0755 -p ${smokepingHome}/cache ${smokepingHome}/data - chown -R ${cfg.user} ${smokepingHome} + rm -f ${smokepingHome}/cropper + ln -s ${cfg.package}/htdocs/cropper ${smokepingHome}/cropper cp ${cgiHome} ${smokepingHome}/smokeping.fcgi ${cfg.package}/bin/smokeping --check --config=${configPath} + ${cfg.package}/bin/smokeping --static --config=${configPath} + chown -R ${cfg.user} ${smokepingHome} ''; script = ''${cfg.package}/bin/smokeping --config=${configPath} --nodaemon''; }; @@ -253,8 +302,9 @@ in wantedBy = [ "multi-user.target"]; requires = [ "smokeping.service"]; partOf = [ "smokeping.service"]; - path = with pkgs; [ bash rrdtool smokeping ]; - script = ''${pkgs.thttpd}/bin/thttpd -u ${cfg.user} -c "**.fcgi" -d ${smokepingHome} -p ${builtins.toString cfg.port} -D''; + path = with pkgs; [ bash rrdtool smokeping thttpd ]; + script = ''thttpd -u ${cfg.user} -c "**.fcgi" -d ${smokepingHome} -p ${builtins.toString cfg.port} -D -nos''; + serviceConfig.Restart = "always"; }; }; } diff --git a/nixos/modules/services/networking/softether.nix b/nixos/modules/services/networking/softether.nix index 5e49efc3aa3..16530078b97 100644 --- a/nixos/modules/services/networking/softether.nix +++ b/nixos/modules/services/networking/softether.nix @@ -63,7 +63,6 @@ in ]; systemd.services."softether-init" = { description = "SoftEther VPN services initial task"; - wantedBy = [ "network-interfaces.target" ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = false; @@ -84,8 +83,9 @@ in (mkIf (cfg.vpnserver.enable) { systemd.services.vpnserver = { description = "SoftEther VPN Server"; - after = [ "softether-init.service" ]; - wantedBy = [ "network-interfaces.target" ]; + after = [ "softether-init.service" "network.target" ]; + wants = [ "softether-init.service" ]; + wantedBy = [ "multi-user.target" ]; serviceConfig = { Type = "forking"; ExecStart = "${pkg}/bin/vpnserver start"; @@ -104,8 +104,9 @@ in (mkIf (cfg.vpnbridge.enable) { systemd.services.vpnbridge = { description = "SoftEther VPN Bridge"; - after = [ "softether-init.service" ]; - wantedBy = [ "network-interfaces.target" ]; + after = [ "softether-init.service" "network.target" ]; + wants = [ "softether-init.service" ]; + wantedBy = [ "multi-user.target" ]; serviceConfig = { Type = "forking"; ExecStart = "${pkg}/bin/vpnbridge start"; @@ -124,8 +125,9 @@ in (mkIf (cfg.vpnclient.enable) { systemd.services.vpnclient = { description = "SoftEther VPN Client"; - after = [ "softether-init.service" ]; - wantedBy = [ "network-interfaces.target" ]; + after = [ "softether-init.service" "network.target" ]; + wants = [ "softether-init.service" ]; + wantedBy = [ "multi-user.target" ]; serviceConfig = { Type = "forking"; ExecStart = "${pkg}/bin/vpnclient start"; diff --git a/nixos/modules/services/networking/ssh/lshd.nix b/nixos/modules/services/networking/ssh/lshd.nix index 661a6a52463..eca599afb33 100644 --- a/nixos/modules/services/networking/ssh/lshd.nix +++ b/nixos/modules/services/networking/ssh/lshd.nix @@ -120,7 +120,7 @@ in systemd.services.lshd = { description = "GNU lshd SSH2 daemon"; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix index f900ef494ab..80659f19c59 100644 --- a/nixos/modules/services/networking/ssh/sshd.nix +++ b/nixos/modules/services/networking/ssh/sshd.nix @@ -85,7 +85,7 @@ in forwardX11 = mkOption { type = types.bool; - default = cfgc.setXAuthLocation; + default = false; description = '' Whether to allow X11 connections to be forwarded. ''; @@ -102,8 +102,8 @@ in }; permitRootLogin = mkOption { - default = "without-password"; - type = types.enum ["yes" "without-password" "forced-commands-only" "no"]; + default = "prohibit-password"; + type = types.enum ["yes" "without-password" "prohibit-password" "forced-commands-only" "no"]; description = '' Whether the root user can login using ssh. ''; @@ -129,7 +129,24 @@ in }; listenAddresses = mkOption { - type = types.listOf types.optionSet; + type = with types; listOf (submodule { + 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. + ''; + }; + }; + }); default = []; example = [ { addr = "192.168.3.1"; port = 22; } { addr = "0.0.0.0"; port = 64022; } ]; description = '' @@ -140,22 +157,6 @@ in 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 { @@ -239,7 +240,7 @@ in systemd = let - service = + sshd-service = { description = "SSH Daemon"; wantedBy = optional (!cfg.startWhenNeeded) "multi-user.target"; @@ -250,16 +251,8 @@ in environment.LD_LIBRARY_PATH = nssModulesPath; - preStart = - '' - mkdir -m 0755 -p /etc/ssh - - ${flip concatMapStrings cfg.hostKeys (k: '' - if ! [ -f "${k.path}" ]; then - ssh-keygen -t "${k.type}" ${if k ? bits then "-b ${toString k.bits}" else ""} -f "${k.path}" -N "" - fi - '')} - ''; + wants = [ "sshd-keygen.service" ]; + after = [ "sshd-keygen.service" ]; serviceConfig = { ExecStart = @@ -271,10 +264,29 @@ in StandardInput = "socket"; } else { Restart = "always"; - Type = "forking"; - PIDFile = "/run/sshd.pid"; + Type = "simple"; }); }; + + sshd-keygen-service = + { description = "SSH Host Key Generation"; + path = [ cfgc.package ]; + script = + '' + mkdir -m 0755 -p /etc/ssh + ${flip concatMapStrings cfg.hostKeys (k: '' + if ! [ -f "${k.path}" ]; then + ssh-keygen -t "${k.type}" ${if k ? bits then "-b ${toString k.bits}" else ""} -f "${k.path}" -N "" + fi + '')} + ''; + + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = "yes"; + }; + }; + in if cfg.startWhenNeeded then { @@ -286,11 +298,13 @@ in socketConfig.Accept = true; }; - services."sshd@" = service; + services.sshd-keygen = sshd-keygen-service; + services."sshd@" = sshd-service; } else { - services.sshd = service; + services.sshd-keygen = sshd-keygen-service; + services.sshd = sshd-service; }; @@ -307,8 +321,6 @@ in services.openssh.extraConfig = mkOrder 0 '' - PidFile /run/sshd.pid - Protocol 2 UsePAM yes diff --git a/nixos/modules/services/networking/supplicant.nix b/nixos/modules/services/networking/supplicant.nix index 16c4ee7e33b..0c459fb1dd0 100644 --- a/nixos/modules/services/networking/supplicant.nix +++ b/nixos/modules/services/networking/supplicant.nix @@ -34,7 +34,8 @@ let ''; in { description = "Supplicant ${iface}${optionalString (iface=="WLAN"||iface=="LAN") " %I"}"; - wantedBy = [ "network.target" ]; + wantedBy = [ "multi-user.target" ] ++ deps; + wants = [ "network.target" ]; bindsTo = deps; after = deps; before = [ "network.target" ]; @@ -75,7 +76,107 @@ in options = { networking.supplicant = mkOption { - type = types.attrsOf types.optionSet; + type = with types; attrsOf (submodule { + options = { + + configFile = { + + path = mkOption { + type = types.path; + example = literalExample "/etc/wpa_supplicant.conf"; + description = '' + External <literal>wpa_supplicant.conf</literal> configuration file. + The configuration options defined declaratively within <literal>networking.supplicant</literal> have + precedence over options defined in <literal>configFile</literal>. + ''; + }; + + writable = mkOption { + type = types.bool; + default = false; + description = '' + Whether the configuration file at <literal>configFile.path</literal> should be written to by + <literal>wpa_supplicant</literal>. + ''; + }; + + }; + + extraConf = mkOption { + type = types.lines; + default = ""; + example = '' + ap_scan=1 + device_name=My-NixOS-Device + device_type=1-0050F204-1 + driver_param=use_p2p_group_interface=1 + disable_scan_offload=1 + p2p_listen_reg_class=81 + p2p_listen_channel=1 + p2p_oper_reg_class=81 + p2p_oper_channel=1 + manufacturer=NixOS + model_name=NixOS_Unstable + model_number=2015 + ''; + description = '' + Configuration options for <literal>wpa_supplicant.conf</literal>. + Options defined here have precedence over options in <literal>configFile</literal>. + NOTE: Do not write sensitive data into <literal>extraConf</literal> as it will + be world-readable in the <literal>nix-store</literal>. For sensitive information + use the <literal>configFile</literal> instead. + ''; + }; + + extraCmdArgs = mkOption { + type = types.str; + default = ""; + example = "-e/var/run/wpa_supplicant/entropy.bin"; + description = + "Command line arguments to add when executing <literal>wpa_supplicant</literal>."; + }; + + driver = mkOption { + type = types.nullOr types.str; + default = "nl80211,wext"; + description = "Force a specific wpa_supplicant driver."; + }; + + bridge = mkOption { + type = types.str; + default = ""; + description = "Name of the bridge interface that wpa_supplicant should listen at."; + }; + + userControlled = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Allow normal users to control wpa_supplicant through wpa_gui or wpa_cli. + This is useful for laptop users that switch networks a lot and don't want + to depend on a large package such as NetworkManager just to pick nearby + access points. + ''; + }; + + socketDir = mkOption { + type = types.str; + default = "/var/run/wpa_supplicant"; + description = "Directory of sockets for controlling wpa_supplicant."; + }; + + group = mkOption { + type = types.str; + default = "wheel"; + example = "network"; + description = "Members of this group can control wpa_supplicant."; + }; + + }; + }; + }); default = { }; @@ -109,107 +210,6 @@ in service that can be accessed through <literal>D-Bus</literal>. ''; - options = { - - configFile = { - - path = mkOption { - type = types.path; - example = literalExample "/etc/wpa_supplicant.conf"; - description = '' - External <literal>wpa_supplicant.conf</literal> configuration file. - The configuration options defined declaratively within <literal>networking.supplicant</literal> have - precedence over options defined in <literal>configFile</literal>. - ''; - }; - - writable = mkOption { - type = types.bool; - default = false; - description = '' - Whether the configuration file at <literal>configFile.path</literal> should be written to by - <literal>wpa_supplicant</literal>. - ''; - }; - - }; - - extraConf = mkOption { - type = types.lines; - default = ""; - example = '' - ap_scan=1 - device_name=My-NixOS-Device - device_type=1-0050F204-1 - driver_param=use_p2p_group_interface=1 - disable_scan_offload=1 - p2p_listen_reg_class=81 - p2p_listen_channel=1 - p2p_oper_reg_class=81 - p2p_oper_channel=1 - manufacturer=NixOS - model_name=NixOS_Unstable - model_number=2015 - ''; - description = '' - Configuration options for <literal>wpa_supplicant.conf</literal>. - Options defined here have precedence over options in <literal>configFile</literal>. - NOTE: Do not write sensitive data into <literal>extraConf</literal> as it will - be world-readable in the <literal>nix-store</literal>. For sensitive information - use the <literal>configFile</literal> instead. - ''; - }; - - extraCmdArgs = mkOption { - type = types.str; - default = ""; - example = "-e/var/run/wpa_supplicant/entropy.bin"; - description = - "Command line arguments to add when executing <literal>wpa_supplicant</literal>."; - }; - - driver = mkOption { - type = types.nullOr types.str; - default = "nl80211,wext"; - description = "Force a specific wpa_supplicant driver."; - }; - - bridge = mkOption { - type = types.str; - default = ""; - description = "Name of the bridge interface that wpa_supplicant should listen at."; - }; - - userControlled = { - - enable = mkOption { - type = types.bool; - default = false; - description = '' - Allow normal users to control wpa_supplicant through wpa_gui or wpa_cli. - This is useful for laptop users that switch networks a lot and don't want - to depend on a large package such as NetworkManager just to pick nearby - access points. - ''; - }; - - socketDir = mkOption { - type = types.str; - default = "/var/run/wpa_supplicant"; - description = "Directory of sockets for controlling wpa_supplicant."; - }; - - group = mkOption { - type = types.str; - default = "wheel"; - example = "network"; - description = "Members of this group can control wpa_supplicant."; - }; - - }; - - }; - }; }; diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix index 8a430734319..dcdc203bdc6 100644 --- a/nixos/modules/services/networking/syncthing.nix +++ b/nixos/modules/services/networking/syncthing.nix @@ -3,46 +3,11 @@ with lib; let - cfg = config.services.syncthing; defaultUser = "syncthing"; - - header = { - description = "Syncthing service"; - after = [ "network.target" ]; - environment = { - STNORESTART = "yes"; - STNOUPGRADE = "yes"; - inherit (cfg) all_proxy; - } // config.networking.proxy.envVars; - }; - - service = { - Restart = "on-failure"; - SuccessExitStatus = "2 3 4"; - RestartForceExitStatus="3 4"; - }; - - iNotifyHeader = { - description = "Syncthing Inotify File Watcher service"; - after = [ "network.target" "syncthing.service" ]; - requires = [ "syncthing.service" ]; - }; - - iNotifyService = { - SuccessExitStatus = "2"; - RestartForceExitStatus = "3"; - Restart = "on-failure"; - }; - -in - -{ - +in { ###### interface - options = { - services.syncthing = { enable = mkEnableOption '' @@ -100,6 +65,19 @@ in ''; }; + openDefaultPorts = mkOption { + type = types.bool; + default = false; + example = literalExample "true"; + description = '' + Open the default ports in the firewall: + - TCP 22000 for transfers + - UDP 21027 for discovery + If multiple users are running syncthing on this machine, you will need to manually open a set of ports for each instance and leave this disabled. + Alternatively, if are running only a single instance on this machine using the default ports, enable this. + ''; + }; + package = mkOption { type = types.package; default = pkgs.syncthing; @@ -117,6 +95,14 @@ in config = mkIf cfg.enable { + networking.firewall = mkIf cfg.openDefaultPorts { + allowedTCPPorts = [ 22000 ]; + allowedUDPPorts = [ 21027 ]; + }; + + systemd.packages = [ pkgs.syncthing ] + ++ lib.optional cfg.useInotify pkgs.syncthing-inotify; + users = mkIf (cfg.user == defaultUser) { extraUsers."${defaultUser}" = { group = cfg.group; @@ -131,39 +117,44 @@ in }; systemd.services = { - syncthing = mkIf cfg.systemService (header // { - wants = mkIf cfg.useInotify [ "syncthing-inotify.service" ]; - wantedBy = [ "multi-user.target" ]; - serviceConfig = service // { - User = cfg.user; - Group = cfg.group; - PermissionsStartOnly = true; - ExecStart = "${cfg.package}/bin/syncthing -no-browser -home=${cfg.dataDir}"; - }; - }); - - syncthing-inotify = mkIf (cfg.systemService && cfg.useInotify) (iNotifyHeader // { + syncthing = mkIf cfg.systemService { + description = "Syncthing service"; + after = [ "network.target" ]; + environment = { + STNORESTART = "yes"; + STNOUPGRADE = "yes"; + inherit (cfg) all_proxy; + } // config.networking.proxy.envVars; + wants = mkIf cfg.useInotify [ "syncthing-inotify.service" ]; wantedBy = [ "multi-user.target" ]; - serviceConfig = iNotifyService // { + serviceConfig = { + Restart = "on-failure"; + SuccessExitStatus = "2 3 4"; + RestartForceExitStatus="3 4"; User = cfg.user; - ExecStart = "${pkgs.syncthing-inotify.bin}/bin/syncthing-inotify -home=${cfg.dataDir} -logflags=0"; + Group = cfg.group; + PermissionsStartOnly = true; + ExecStart = "${cfg.package}/bin/syncthing -no-browser -home=${cfg.dataDir}"; }; - }); - }; + }; - systemd.user.services = { - syncthing = header // { - serviceConfig = service // { - ExecStart = "${cfg.package}/bin/syncthing -no-browser"; - }; + syncthing-resume = { + wantedBy = [ "suspend.target" ]; }; - syncthing-inotify = mkIf cfg.useInotify (iNotifyHeader // { - serviceConfig = iNotifyService // { - ExecStart = "${pkgs.syncthing-inotify.bin}/bin/syncthing-inotify -logflags=0"; + syncthing-inotify = mkIf (cfg.systemService && cfg.useInotify) { + description = "Syncthing Inotify File Watcher service"; + after = [ "network.target" "syncthing.service" ]; + requires = [ "syncthing.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + SuccessExitStatus = "2"; + RestartForceExitStatus = "3"; + Restart = "on-failure"; + User = cfg.user; + ExecStart = "${pkgs.syncthing-inotify.bin}/bin/syncthing-inotify -home=${cfg.dataDir} -logflags=0"; }; - }); + }; }; - }; } diff --git a/nixos/modules/services/networking/tcpcrypt.nix b/nixos/modules/services/networking/tcpcrypt.nix index 267653abce0..2f304165eb4 100644 --- a/nixos/modules/services/networking/tcpcrypt.nix +++ b/nixos/modules/services/networking/tcpcrypt.nix @@ -39,7 +39,7 @@ in description = "tcpcrypt"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; path = [ pkgs.iptables pkgs.tcpcrypt pkgs.procps ]; diff --git a/nixos/modules/services/networking/tftpd.nix b/nixos/modules/services/networking/tftpd.nix index 9b3cc6b8ec4..c9c0a2b321d 100644 --- a/nixos/modules/services/networking/tftpd.nix +++ b/nixos/modules/services/networking/tftpd.nix @@ -13,12 +13,13 @@ with lib; default = false; description = '' Whether to enable tftpd, a Trivial File Transfer Protocol server. + The server will be run as an xinetd service. ''; }; services.tftpd.path = mkOption { type = types.path; - default = "/home/tftp"; + default = "/srv/tftp"; description = '' Where the tftp server files are stored. ''; diff --git a/nixos/modules/services/networking/tinc.nix b/nixos/modules/services/networking/tinc.nix index 8da0f817ae2..f8e68fda7fc 100644 --- a/nixos/modules/services/networking/tinc.nix +++ b/nixos/modules/services/networking/tinc.nix @@ -18,94 +18,96 @@ in networks = mkOption { default = { }; - type = types.loaOf types.optionSet; + type = with types; loaOf (submodule { + options = { + + extraConfig = mkOption { + default = ""; + type = types.lines; + description = '' + Extra lines to add to the tinc service configuration file. + ''; + }; + + name = mkOption { + default = null; + type = types.nullOr types.str; + description = '' + The name of the node which is used as an identifier when communicating + with the remote nodes in the mesh. If null then the hostname of the system + is used. + ''; + }; + + ed25519PrivateKeyFile = mkOption { + default = null; + type = types.nullOr types.path; + description = '' + Path of the private ed25519 keyfile. + ''; + }; + + debugLevel = mkOption { + default = 0; + type = types.addCheck types.int (l: l >= 0 && l <= 5); + description = '' + The amount of debugging information to add to the log. 0 means little + logging while 5 is the most logging. <command>man tincd</command> for + more details. + ''; + }; + + hosts = mkOption { + default = { }; + type = types.loaOf types.lines; + description = '' + The name of the host in the network as well as the configuration for that host. + This name should only contain alphanumerics and underscores. + ''; + }; + + interfaceType = mkOption { + default = "tun"; + type = types.enum [ "tun" "tap" ]; + description = '' + The type of virtual interface used for the network connection + ''; + }; + + listenAddress = mkOption { + default = null; + type = types.nullOr types.str; + description = '' + The ip adress to bind to. + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.tinc_pre; + defaultText = "pkgs.tinc_pre"; + description = '' + The package to use for the tinc daemon's binary. + ''; + }; + + chroot = mkOption { + default = true; + type = types.bool; + description = '' + Change process root directory to the directory where the config file is located (/etc/tinc/netname/), for added security. + The chroot is performed after all the initialization is done, after writing pid files and opening network sockets. + + Note that tinc can't run scripts anymore (such as tinc-down or host-up), unless it is setup to be runnable inside chroot environment. + ''; + }; + }; + }); + description = '' Defines the tinc networks which will be started. Each network invokes a different daemon. ''; - options = { - - extraConfig = mkOption { - default = ""; - type = types.lines; - description = '' - Extra lines to add to the tinc service configuration file. - ''; - }; - - name = mkOption { - default = null; - type = types.nullOr types.str; - description = '' - The name of the node which is used as an identifier when communicating - with the remote nodes in the mesh. If null then the hostname of the system - is used. - ''; - }; - - ed25519PrivateKeyFile = mkOption { - default = null; - type = types.nullOr types.path; - description = '' - Path of the private ed25519 keyfile. - ''; - }; - - debugLevel = mkOption { - default = 0; - type = types.addCheck types.int (l: l >= 0 && l <= 5); - description = '' - The amount of debugging information to add to the log. 0 means little - logging while 5 is the most logging. <command>man tincd</command> for - more details. - ''; - }; - - hosts = mkOption { - default = { }; - type = types.loaOf types.lines; - description = '' - The name of the host in the network as well as the configuration for that host. - This name should only contain alphanumerics and underscores. - ''; - }; - - interfaceType = mkOption { - default = "tun"; - type = types.addCheck types.str (n: n == "tun" || n == "tap"); - description = '' - The type of virtual interface used for the network connection - ''; - }; - - listenAddress = mkOption { - default = null; - type = types.nullOr types.str; - description = '' - The ip adress to bind to. - ''; - }; - - package = mkOption { - type = types.package; - default = pkgs.tinc_pre; - defaultText = "pkgs.tinc_pre"; - description = '' - The package to use for the tinc daemon's binary. - ''; - }; - - chroot = mkOption { - default = true; - type = types.bool; - description = '' - Change process root directory to the directory where the config file is located (/etc/tinc/netname/), for added security. - The chroot is performed after all the initialization is done, after writing pid files and opening network sockets. - - Note that tinc can't run scripts anymore (such as tinc-down or host-up), unless it is setup to be runnable inside chroot environment. - ''; - }; - }; }; }; @@ -149,8 +151,8 @@ in ("tinc.${network}") ({ description = "Tinc Daemon - ${network}"; - wantedBy = [ "network.target" ]; - after = [ "network-interfaces.target" ]; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; path = [ data.package ]; restartTriggers = [ config.environment.etc."tinc/${network}/tinc.conf".source ] ++ mapAttrsToList (host: _ : config.environment.etc."tinc/${network}/hosts/${host}".source) data.hosts; diff --git a/nixos/modules/services/networking/toxvpn.nix b/nixos/modules/services/networking/toxvpn.nix index c38424c8e27..911836fdee4 100644 --- a/nixos/modules/services/networking/toxvpn.nix +++ b/nixos/modules/services/networking/toxvpn.nix @@ -25,8 +25,8 @@ with lib; systemd.services.toxvpn = { description = "toxvpn daemon"; - requires = [ "network-online.target" ]; # consider replacing by NetworkManager-wait-online.service wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; preStart = '' mkdir -p /run/toxvpn || true diff --git a/nixos/modules/services/networking/unbound.nix b/nixos/modules/services/networking/unbound.nix index ed0744c44cc..f3a04d97c98 100644 --- a/nixos/modules/services/networking/unbound.nix +++ b/nixos/modules/services/networking/unbound.nix @@ -12,9 +12,17 @@ let interfaces = concatMapStrings (x: " interface: ${x}\n") cfg.interfaces; - forward = optionalString (length cfg.forwardAddresses != 0) - "forward-zone:\n name: .\n" + - concatMapStrings (x: " forward-addr: ${x}\n") cfg.forwardAddresses; + isLocalAddress = x: substring 0 3 x == "::1" || substring 0 9 x == "127.0.0.1"; + + forward = + optionalString (any isLocalAddress cfg.forwardAddresses) '' + do-not-query-localhost: no + '' + + optionalString (cfg.forwardAddresses != []) '' + forward-zone: + name: . + '' + + concatMapStringsSep "\n" (x: " forward-addr: ${x}") cfg.forwardAddresses; rootTrustAnchorFile = "${stateDir}/root.key"; @@ -71,8 +79,12 @@ in extraConfig = mkOption { default = ""; - type = types.str; - description = "Extra lines of unbound config."; + type = types.lines; + description = '' + Extra unbound config. See + <citerefentry><refentrytitle>unbound.conf</refentrytitle><manvolnum>8 + </manvolnum></citerefentry>. + ''; }; }; @@ -84,12 +96,9 @@ in environment.systemPackages = [ pkgs.unbound ]; - users.extraUsers = singleton { - name = "unbound"; - uid = config.ids.uids.unbound; + users.users.unbound = { description = "unbound daemon user"; - home = stateDir; - createHome = true; + isSystemUser = true; }; systemd.services.unbound = { @@ -107,12 +116,16 @@ in chown unbound ${stateDir} ${rootTrustAnchorFile} ''} touch ${stateDir}/dev/random - ${pkgs.utillinux}/bin/mount --bind -n /dev/random ${stateDir}/dev/random + ${pkgs.utillinux}/bin/mount --bind -n /dev/urandom ${stateDir}/dev/random ''; serviceConfig = { ExecStart = "${pkgs.unbound}/bin/unbound -d -c ${stateDir}/unbound.conf"; ExecStopPost="${pkgs.utillinux}/bin/umount ${stateDir}/dev/random"; + + ProtectSystem = true; + ProtectHome = true; + PrivateDevices = true; }; }; diff --git a/nixos/modules/services/networking/vsftpd.nix b/nixos/modules/services/networking/vsftpd.nix index 7ec484941ed..deff645d9bf 100644 --- a/nixos/modules/services/networking/vsftpd.nix +++ b/nixos/modules/services/networking/vsftpd.nix @@ -100,6 +100,10 @@ let seccomp_sandbox=NO ''} anon_umask=${cfg.anonymousUmask} + ${optionalString cfg.anonymousUser '' + anon_root=${cfg.anonymousUserHome} + ''} + ${cfg.extraConfig} ''; in @@ -163,6 +167,13 @@ in description = "Anonymous write umask."; }; + extraConfig = mkOption { + type = types.lines; + default = ""; + example = "ftpd_banner=Hello"; + description = "Extra configuration to add at the bottom of the generated configuration file."; + }; + } // (listToAttrs (catAttrs "nixosOption" optionDescription)); }; diff --git a/nixos/modules/services/networking/wicd.nix b/nixos/modules/services/networking/wicd.nix index 9e5a437b485..03c6bd28aab 100644 --- a/nixos/modules/services/networking/wicd.nix +++ b/nixos/modules/services/networking/wicd.nix @@ -26,7 +26,9 @@ with lib; environment.systemPackages = [pkgs.wicd]; systemd.services.wicd = { - after = [ "network-interfaces.target" ]; + after = [ "network-pre.target" ]; + before = [ "network.target" ]; + wants = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; script = "${pkgs.wicd}/sbin/wicd -f"; }; diff --git a/nixos/modules/services/networking/wireguard.nix b/nixos/modules/services/networking/wireguard.nix new file mode 100644 index 00000000000..368d89e2e32 --- /dev/null +++ b/nixos/modules/services/networking/wireguard.nix @@ -0,0 +1,231 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.networking.wireguard; + + kernel = config.boot.kernelPackages; + + # interface options + + interfaceOpts = { name, ... }: { + + options = { + + ips = mkOption { + example = [ "192.168.2.1/24" ]; + default = []; + type = with types; listOf str; + description = "The IP addresses of the interface."; + }; + + privateKey = mkOption { + example = "yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk="; + type = types.str; + description = "Base64 private key generated by wg genkey."; + }; + + presharedKey = mkOption { + default = null; + example = "rVXs/Ni9tu3oDBLS4hOyAUAa1qTWVA3loR8eL20os3I="; + type = with types; nullOr str; + description = '' + base64 preshared key generated by wg genpsk. Optional, + and may be omitted. This option adds an additional layer of + symmetric-key cryptography to be mixed into the already existing + public-key cryptography, for post-quantum resistance. + ''; + }; + + listenPort = mkOption { + default = null; + type = with types; nullOr int; + example = 51820; + description = '' + 16-bit port for listening. Optional; if not specified, + automatically generated based on interface name. + ''; + }; + + preSetup = mkOption { + example = literalExample ['' + ${pkgs.iproute}/bin/ip netns add foo + '']; + default = []; + type = with types; listOf str; + description = '' + A list of commands called at the start of the interface setup. + ''; + }; + + postSetup = mkOption { + example = literalExample ['' + ${pkgs.bash} -c 'printf "nameserver 10.200.100.1" | ${pkgs.openresolv}/bin/resolvconf -a wg0 -m 0' + '']; + default = []; + type = with types; listOf str; + description = "A list of commands called at the end of the interface setup."; + }; + + postShutdown = mkOption { + example = literalExample ["${pkgs.openresolv}/bin/resolvconf -d wg0"]; + default = []; + type = with types; listOf str; + description = "A list of commands called after shutting down the interface."; + }; + + peers = mkOption { + default = []; + description = "Peers linked to the interface."; + type = with types; listOf (submodule peerOpts); + }; + + }; + + }; + + # peer options + + peerOpts = { + + options = { + + publicKey = mkOption { + example = "xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg="; + type = types.str; + description = "The base64 public key the peer."; + }; + + allowedIPs = mkOption { + example = [ "10.192.122.3/32" "10.192.124.1/24" ]; + type = with types; listOf str; + description = ''List of IP (v4 or v6) addresses with CIDR masks from + which this peer is allowed to send incoming traffic and to which + outgoing traffic for this peer is directed. The catch-all 0.0.0.0/0 may + be specified for matching all IPv4 addresses, and ::/0 may be specified + for matching all IPv6 addresses.''; + }; + + endpoint = mkOption { + default = null; + example = "demo.wireguard.io:12913"; + type = with types; nullOr str; + description = ''Endpoint IP or hostname of the peer, followed by a colon, + and then a port number of the peer.''; + }; + + persistentKeepalive = mkOption { + default = null; + type = with types; nullOr int; + example = 25; + description = ''This is optional and is by default off, because most + users will not need it. It represents, in seconds, between 1 and 65535 + inclusive, how often to send an authenticated empty packet to the peer, + for the purpose of keeping a stateful firewall or NAT mapping valid + persistently. For example, if the interface very rarely sends traffic, + but it might at anytime receive traffic from a peer, and it is behind + NAT, the interface might benefit from having a persistent keepalive + interval of 25 seconds; however, most users will not need this.''; + }; + + }; + + }; + + generateConf = name: values: pkgs.writeText "wireguard-${name}.conf" '' + [Interface] + PrivateKey = ${values.privateKey} + ${optionalString (values.presharedKey != null) "PresharedKey = ${values.presharedKey}"} + ${optionalString (values.listenPort != null) "ListenPort = ${toString values.listenPort}"} + + ${concatStringsSep "\n\n" (map (peer: '' + [Peer] + PublicKey = ${peer.publicKey} + ${optionalString (peer.allowedIPs != []) "AllowedIPs = ${concatStringsSep ", " peer.allowedIPs}"} + ${optionalString (peer.endpoint != null) "Endpoint = ${peer.endpoint}"} + ${optionalString (peer.persistentKeepalive != null) "PersistentKeepalive = ${toString peer.persistentKeepalive}"} + '') values.peers)} + ''; + + ipCommand = "${pkgs.iproute}/bin/ip"; + wgCommand = "${pkgs.wireguard}/bin/wg"; + + generateUnit = name: values: + nameValuePair "wireguard-${name}" + { + description = "WireGuard Tunnel - ${name}"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = lib.flatten([ + values.preSetup + + "-${ipCommand} link del dev ${name}" + "${ipCommand} link add dev ${name} type wireguard" + "${wgCommand} setconf ${name} ${generateConf name values}" + + (map (ip: + ''${ipCommand} address add ${ip} dev ${name}'' + ) values.ips) + + "${ipCommand} link set up dev ${name}" + + (flatten (map (peer: (map (ip: + "${ipCommand} route add ${ip} dev ${name}" + ) peer.allowedIPs)) values.peers)) + + values.postSetup + ]); + + ExecStop = [ ''${ipCommand} link del dev "${name}"'' ] ++ values.postShutdown; + }; + }; + +in + +{ + + ###### interface + + options = { + + networking.wireguard = { + + interfaces = mkOption { + description = "Wireguard interfaces."; + default = {}; + example = { + wg0 = { + ips = [ "192.168.20.4/24" ]; + privateKey = "yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk="; + peers = [ + { allowedIPs = [ "192.168.20.1/32" ]; + publicKey = "xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg="; + endpoint = "demo.wireguard.io:12913"; } + ]; + }; + }; + type = with types; attrsOf (submodule interfaceOpts); + }; + + }; + + }; + + + ###### implementation + + config = mkIf (cfg.interfaces != {}) { + + boot.extraModulePackages = [ kernel.wireguard ]; + environment.systemPackages = [ pkgs.wireguard ]; + + systemd.services = mapAttrs' generateUnit cfg.interfaces; + + }; + +} diff --git a/nixos/modules/services/networking/wpa_supplicant.nix b/nixos/modules/services/networking/wpa_supplicant.nix index de99ce4f026..c91ba91fcb4 100644 --- a/nixos/modules/services/networking/wpa_supplicant.nix +++ b/nixos/modules/services/networking/wpa_supplicant.nix @@ -12,11 +12,13 @@ let psk = if networkConfig.psk != null then ''"${networkConfig.psk}"'' else networkConfig.pskRaw; + priority = networkConfig.priority; in '' network={ ssid="${ssid}" ${optionalString (psk != null) ''psk=${psk}''} ${optionalString (psk == null) ''key_mgmt=NONE''} + ${optionalString (priority != null) ''priority=${toString priority}''} } '') cfg.networks)} '' else "/etc/wpa_supplicant.conf"; @@ -68,6 +70,19 @@ in { Mutually exclusive with <varname>psk</varname>. ''; }; + priority = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + By default, all networks will get same priority group (0). If some of the + networks are more desirable, this field can be used to change the order in + which wpa_supplicant goes through the networks when selecting a BSS. The + priority groups will be iterated in decreasing priority (i.e., the larger the + priority value, the sooner the network is matched against the scan results). + Within each priority group, networks will be selected based on security + policy, signal strength, etc. + ''; + }; }; }); description = '' @@ -128,9 +143,11 @@ in { in { description = "WPA Supplicant"; - after = [ "network-interfaces.target" ] ++ lib.concatMap deviceUnit ifaces; + after = lib.concatMap deviceUnit ifaces; + before = [ "network.target" ]; + wants = [ "network.target" ]; requires = lib.concatMap deviceUnit ifaces; - wantedBy = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; path = [ pkgs.wpa_supplicant ]; diff --git a/nixos/modules/services/networking/xinetd.nix b/nixos/modules/services/networking/xinetd.nix index 08680b51780..00224502780 100644 --- a/nixos/modules/services/networking/xinetd.nix +++ b/nixos/modules/services/networking/xinetd.nix @@ -65,71 +65,73 @@ in A list of services provided by xinetd. ''; - type = types.listOf types.optionSet; + type = with types; listOf (submodule ({ + + options = { + + name = mkOption { + type = types.string; + example = "login"; + description = "Name of the service."; + }; + + protocol = mkOption { + type = types.string; + default = "tcp"; + description = + "Protocol of the service. Usually <literal>tcp</literal> or <literal>udp</literal>."; + }; + + port = mkOption { + type = types.int; + default = 0; + example = 123; + description = "Port number of the service."; + }; + + user = mkOption { + type = types.string; + default = "nobody"; + description = "User account for the service"; + }; + + server = mkOption { + type = types.string; + example = "/foo/bin/ftpd"; + description = "Path of the program that implements the service."; + }; + + serverArgs = mkOption { + type = types.string; + default = ""; + description = "Command-line arguments for the server program."; + }; + + flags = mkOption { + type = types.string; + default = ""; + description = ""; + }; + + unlisted = mkOption { + type = types.bool; + default = false; + description = '' + Whether this server is listed in + <filename>/etc/services</filename>. If so, the port + number can be omitted. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = "Extra configuration-lines added to the section of the service."; + }; - options = { - - name = mkOption { - type = types.string; - example = "login"; - description = "Name of the service."; - }; - - protocol = mkOption { - type = types.string; - default = "tcp"; - description = - "Protocol of the service. Usually <literal>tcp</literal> or <literal>udp</literal>."; - }; - - port = mkOption { - type = types.int; - default = 0; - example = 123; - description = "Port number of the service."; - }; - - user = mkOption { - type = types.string; - default = "nobody"; - description = "User account for the service"; - }; - - server = mkOption { - type = types.string; - example = "/foo/bin/ftpd"; - description = "Path of the program that implements the service."; - }; - - serverArgs = mkOption { - type = types.string; - default = ""; - description = "Command-line arguments for the server program."; - }; - - flags = mkOption { - type = types.string; - default = ""; - description = ""; - }; - - unlisted = mkOption { - type = types.bool; - default = false; - description = '' - Whether this server is listed in - <filename>/etc/services</filename>. If so, the port - number can be omitted. - ''; - }; - - extraConfig = mkOption { - type = types.string; - default = ""; - description = "Extra configuration-lines added to the section of the service."; }; - }; + })); }; @@ -141,7 +143,7 @@ in config = mkIf cfg.enable { systemd.services.xinetd = { description = "xinetd server"; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; path = [ pkgs.xinetd ]; script = "xinetd -syslog daemon -dontfork -stayalive -f ${configFile}"; diff --git a/nixos/modules/services/networking/zerobin.nix b/nixos/modules/services/networking/zerobin.nix index 1c524602f8e..274bbca53fa 100644 --- a/nixos/modules/services/networking/zerobin.nix +++ b/nixos/modules/services/networking/zerobin.nix @@ -86,15 +86,15 @@ in systemd.services.zerobin = { enable = true; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig.ExecStart = "${pkgs.pythonPackages.zerobin}/bin/zerobin ${cfg.listenAddress} ${toString cfg.listenPort} false ${cfg.user} ${cfg.group} ${zerobin_config}"; serviceConfig.PrivateTmp="yes"; serviceConfig.User = cfg.user; serviceConfig.Group = cfg.group; preStart = '' - mkdir -p ${cfg.dataDir} - chown ${cfg.user} ${cfg.dataDir} + mkdir -p ${cfg.dataDir} + chown ${cfg.user} ${cfg.dataDir} ''; }; }; diff --git a/nixos/modules/services/networking/znc.nix b/nixos/modules/services/networking/znc.nix index 196a14dd40e..76ba78ff366 100644 --- a/nixos/modules/services/networking/znc.nix +++ b/nixos/modules/services/networking/znc.nix @@ -26,53 +26,35 @@ let }; # Keep znc.conf in nix store, then symlink or copy into `dataDir`, depending on `mutable`. + notNull = a: ! isNull a; mkZncConf = confOpts: '' - // Also check http://en.znc.in/wiki/Configuration - - AnonIPLimit = 10 - ConnectDelay = 5 - # Add `LoadModule = x` for each module... + Version = 1.6.3 ${concatMapStrings (n: "LoadModule = ${n}\n") confOpts.modules} - MaxBufferSize = 500 - ProtectWebSessions = true - SSLCertFile = ${cfg.dataDir}/znc.pem - ServerThrottle = 30 - Skin = dark-clouds - StatusPrefix = * - Version = 1.2 - - <Listener listener0> - AllowIRC = true - AllowWeb = true + + <Listener l> + Port = ${toString confOpts.port} IPv4 = true - IPv6 = false - Port = ${if confOpts.useSSL then "+" else ""}${toString confOpts.port} + IPv6 = true SSL = ${if confOpts.useSSL then "true" else "false"} </Listener> <User ${confOpts.userName}> + ${confOpts.passBlock} Admin = true - Allow = * - AltNick = ${confOpts.nick}_ - AppendTimestamp = false - AutoClearChanBuffer = false - Buffer = 150 - ChanModes = +stn - DenyLoadMod = false - DenySetBindHost = false - Ident = ident - JoinTries = 10 - MaxJoins = 0 - MaxNetworks = 1 - MultiClients = true Nick = ${confOpts.nick} - PrependTimestamp = true - QuitMsg = Quit + AltNick = ${confOpts.nick}_ + Ident = ${confOpts.nick} RealName = ${confOpts.nick} - TimestampFormat = [%H:%M:%S] ${concatMapStrings (n: "LoadModule = ${n}\n") confOpts.userModules} - - ${confOpts.passBlock} + + ${ lib.concatStringsSep "\n" (lib.mapAttrsToList (name: net: '' + <Network ${name}> + ${concatMapStrings (m: "LoadModule = ${m}\n") net.modules} + Server = ${net.server} ${if net.useSSL then "+" else ""}${toString net.port} + + ${concatMapStrings (c: "<Chan #${c}>\n</Chan>\n") net.channels} + </Network> + '') confOpts.networks) } </User> ${confOpts.extraZncConf} ''; @@ -84,6 +66,62 @@ let else mkZncConf cfg.confOptions; }; + networkOpts = { ... }: { + options = { + server = mkOption { + type = types.str; + example = "chat.freenode.net"; + description = '' + IRC server address. + ''; + }; + + port = mkOption { + type = types.int; + default = 6697; + example = 6697; + description = '' + IRC server port. + ''; + }; + + useSSL = mkOption { + type = types.bool; + default = true; + description = '' + Whether to use SSL to connect to the IRC server. + ''; + }; + + modulePackages = mkOption { + type = types.listOf types.package; + default = []; + example = [ "pkgs.zncModules.push" "pkgs.zncModules.fish" ]; + description = '' + External ZNC modules to build. + ''; + }; + + modules = mkOption { + type = types.listOf types.str; + default = [ "simple_away" ]; + example = literalExample "[ simple_away sasl ]"; + description = '' + ZNC modules to load. + ''; + }; + + channels = mkOption { + type = types.listOf types.str; + default = []; + example = [ "nixos" ]; + description = '' + IRC channels to join. + ''; + }; + }; + }; + in { @@ -111,6 +149,15 @@ in ''; }; + group = mkOption { + default = ""; + example = "users"; + type = types.string; + description = '' + Group to own the ZNCserver process. + ''; + }; + dataDir = mkOption { default = "/var/lib/znc/"; example = "/home/john/.znc/"; @@ -125,27 +172,16 @@ in example = "See: http://wiki.znc.in/Configuration"; type = types.lines; description = '' - The contents of the `znc.conf` file to use when creating it. + Config file as generated with `znc --makeconf` to use for the whole ZNC configuration. If specified, `confOptions` will be ignored, and this value, as-is, will be used. If left empty, a conf file with default values will be used. - Recommended to generate with `znc --makeconf` command. ''; }; - /* 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.str; - default = [ "partyline" "webadmin" "adminlog" "log" ]; + default = [ "webadmin" "adminlog" ]; example = [ "partyline" "webadmin" "adminlog" "log" ]; description = '' A list of modules to include in the `znc.conf` file. @@ -154,8 +190,8 @@ in userModules = mkOption { type = types.listOf types.str; - default = [ ]; - example = [ "fish" "push" ]; + default = [ "chansaver" "controlpanel" ]; + example = [ "chansaver" "controlpanel" "fish" "push" ]; description = '' A list of user modules to include in the `znc.conf` file. ''; @@ -166,9 +202,24 @@ in 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. + The user name used to log in to the ZNC web admin interface. + ''; + }; + + networks = mkOption { + default = { }; + type = with types; loaOf (submodule networkOpts); + description = '' + IRC networks to connect the user to. ''; + example = { + "freenode" = { + server = "chat.freenode.net"; + port = 6697; + ssl = true; + modules = [ "simple_away" ]; + }; + }; }; nick = mkOption { @@ -176,19 +227,16 @@ in example = "john"; type = types.string; description = '' - The IRC nick to use when generating the `znc.conf` file. + The IRC nick. ''; }; passBlock = mkOption { - default = defaultPassBlock; - example = "Must be the block generated by the `znc --makepass` command."; + example = defaultPassBlock; 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. !!! + Generate with znc --makepass. + This is the password used to log in to the ZNC web admin interface. ''; }; @@ -206,7 +254,7 @@ in example = true; type = types.bool; description = '' - Indicates whether the ZNC server should use SSL when listening on the specified port. + Indicates whether the ZNC server should use SSL when listening on the specified port. A self-signed certificate will be generated. ''; }; @@ -214,7 +262,7 @@ in default = ""; type = types.lines; description = '' - Extra config to `znc.conf` file + Extra config to `znc.conf` file. ''; }; }; @@ -265,6 +313,7 @@ in after = [ "network.service" ]; serviceConfig = { User = cfg.user; + Group = cfg.group; Restart = "always"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID"; diff --git a/nixos/modules/services/printing/cupsd.nix b/nixos/modules/services/printing/cupsd.nix index 368d7ac761a..3041dccfd15 100644 --- a/nixos/modules/services/printing/cupsd.nix +++ b/nixos/modules/services/printing/cupsd.nix @@ -75,7 +75,7 @@ let '') cfg.listenAddresses} Listen /var/run/cups/cups.sock - SetEnv PATH ${bindir}/lib/cups/filter:${bindir}/bin + SetEnv PATH /var/lib/cups/path/lib/cups/filter:/var/lib/cups/path/bin DefaultShared ${if cfg.defaultShared then "Yes" else "No"} @@ -310,6 +310,13 @@ in for i in *; do [ ! -e "/var/lib/cups/$i" ] && ln -s "${rootdir}/etc/cups/$i" "/var/lib/cups/$i" done + + #update path reference + [ -L /var/lib/cups/path ] && \ + rm /var/lib/cups/path + [ ! -e /var/lib/cups/path ] && \ + ln -s ${bindir} /var/lib/cups/path + ${optionalString cfg.gutenprint '' if [ -d /var/lib/cups/ppd ]; then ${gutenprint}/bin/cups-genppdupdate -p /var/lib/cups/ppd diff --git a/nixos/modules/services/scheduling/chronos.nix b/nixos/modules/services/scheduling/chronos.nix index db1f0f5f00c..6c39997fec8 100644 --- a/nixos/modules/services/scheduling/chronos.nix +++ b/nixos/modules/services/scheduling/chronos.nix @@ -41,7 +41,7 @@ in { systemd.services.chronos = { description = "Chronos Service"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" "zookeeper.service" ]; + after = [ "network.target" "zookeeper.service" ]; serviceConfig = { ExecStart = "${pkgs.chronos}/bin/chronos --master ${cfg.master} --zk_hosts ${concatStringsSep "," cfg.zookeeperHosts} --http_port ${toString cfg.httpPort}"; diff --git a/nixos/modules/services/scheduling/marathon.nix b/nixos/modules/services/scheduling/marathon.nix index 4e837c62dc1..19c9a708f21 100644 --- a/nixos/modules/services/scheduling/marathon.nix +++ b/nixos/modules/services/scheduling/marathon.nix @@ -83,7 +83,7 @@ in { description = "Marathon Service"; environment = cfg.environment; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" "zookeeper.service" "mesos-master.service" "mesos-slave.service" ]; + after = [ "network.target" "zookeeper.service" "mesos-master.service" "mesos-slave.service" ]; serviceConfig = { ExecStart = "${pkgs.marathon}/bin/marathon --master ${cfg.master} --zk zk://${concatStringsSep "," cfg.zookeeperHosts}/marathon --http_port ${toString cfg.httpPort} ${concatStringsSep " " cfg.extraCmdLineOptions}"; diff --git a/nixos/modules/services/search/elasticsearch.nix b/nixos/modules/services/search/elasticsearch.nix index 9299aaac2f7..574f74d547a 100644 --- a/nixos/modules/services/search/elasticsearch.nix +++ b/nixos/modules/services/search/elasticsearch.nix @@ -129,7 +129,7 @@ in { systemd.services.elasticsearch = { description = "Elasticsearch Daemon"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; path = [ pkgs.inetutils ]; environment = { ES_HOME = cfg.dataDir; diff --git a/nixos/modules/services/search/hound.nix b/nixos/modules/services/search/hound.nix new file mode 100644 index 00000000000..a94a851e80e --- /dev/null +++ b/nixos/modules/services/search/hound.nix @@ -0,0 +1,125 @@ +{ config, lib, pkgs, ... }: +with lib; +let + cfg = config.services.hound; +in { + options = { + services.hound = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable the hound code search daemon. + ''; + }; + + user = mkOption { + default = "hound"; + type = types.str; + description = '' + User the hound daemon should execute under. + ''; + }; + + group = mkOption { + default = "hound"; + type = types.str; + description = '' + Group the hound daemon should execute under. + ''; + }; + + extraGroups = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "dialout" ]; + description = '' + List of extra groups that the "hound" user should be a part of. + ''; + }; + + home = mkOption { + default = "/var/lib/hound"; + type = types.path; + description = '' + The path to use as hound's $HOME. If the default user + "hound" is configured then this is the home of the "hound" + user. + ''; + }; + + package = mkOption { + default = pkgs.hound; + defaultText = "pkgs.hound"; + type = types.package; + description = '' + Package for running hound. + ''; + }; + + config = mkOption { + type = types.str; + description = '' + The full configuration of the Hound daemon. Note the dbpath + should be an absolute path to a writable location on disk. + ''; + example = '' + { + "max-concurrent-indexers" : 2, + "dbpath" : "''${services.hound.home}/data", + "repos" : { + "nixpkgs": { + "url" : "https://www.github.com/NixOS/nixpkgs.git" + } + } + } + ''; + }; + + listen = mkOption { + type = types.str; + default = "0.0.0.0:6080"; + example = "127.0.0.1:6080 or just :6080"; + description = '' + Listen on this IP:port / :port + ''; + }; + }; + }; + + config = mkIf cfg.enable { + users.extraGroups = optional (cfg.group == "hound") { + name = "hound"; + gid = config.ids.gids.hound; + }; + + users.extraUsers = optional (cfg.user == "hound") { + name = "hound"; + description = "hound code search"; + createHome = true; + home = cfg.home; + group = cfg.group; + extraGroups = cfg.extraGroups; + uid = config.ids.uids.hound; + }; + + systemd.services.hound = { + description = "Hound Code Search"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + User = cfg.user; + Group = cfg.group; + WorkingDirectory = cfg.home; + ExecStartPre = "${pkgs.git}/bin/git config --global --replace-all http.sslCAinfo /etc/ssl/certs/ca-certificates.crt"; + ExecStart = "${cfg.package}/bin/houndd" + + " -addr ${cfg.listen}" + + " -conf ${pkgs.writeText "hound.json" cfg.config}"; + + }; + path = [ pkgs.git pkgs.mercurial pkgs.openssh ]; + }; + }; + +} diff --git a/nixos/modules/services/search/kibana.nix b/nixos/modules/services/search/kibana.nix index 033b8139d34..d377a6feeb8 100644 --- a/nixos/modules/services/search/kibana.nix +++ b/nixos/modules/services/search/kibana.nix @@ -138,7 +138,7 @@ in { systemd.services.kibana = { description = "Kibana Service"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" "elasticsearch.service" ]; + after = [ "network.target" "elasticsearch.service" ]; environment = { BABEL_CACHE_PATH = "${cfg.dataDir}/.babelcache.json"; }; serviceConfig = { ExecStart = "${cfg.package}/bin/kibana --config ${cfgFile}"; diff --git a/nixos/modules/services/security/clamav.nix b/nixos/modules/services/security/clamav.nix index e4e5c1253b7..b045e140546 100644 --- a/nixos/modules/services/security/clamav.nix +++ b/nixos/modules/services/security/clamav.nix @@ -3,26 +3,37 @@ with lib; let clamavUser = "clamav"; stateDir = "/var/lib/clamav"; - runDir = "/var/run/clamav"; - logDir = "/var/log/clamav"; + runDir = "/run/clamav"; clamavGroup = clamavUser; cfg = config.services.clamav; + pkg = pkgs.clamav; + clamdConfigFile = pkgs.writeText "clamd.conf" '' DatabaseDirectory ${stateDir} LocalSocket ${runDir}/clamd.ctl - LogFile ${logDir}/clamav.log PidFile ${runDir}/clamd.pid + TemporaryDirectory /tmp User clamav + Foreground yes ${cfg.daemon.extraConfig} ''; - pkg = pkgs.clamav.override { freshclamConf = cfg.updater.config; }; + + freshclamConfigFile = pkgs.writeText "freshclam.conf" '' + DatabaseDirectory ${stateDir} + Foreground yes + Checks ${toString cfg.updater.frequency} + + ${cfg.updater.extraConfig} + + DatabaseMirror database.clamav.net + ''; in { options = { services.clamav = { daemon = { - enable = mkEnableOption "clamd daemon"; + enable = mkEnableOption "ClamAV clamd daemon"; extraConfig = mkOption { type = types.lines; @@ -34,16 +45,27 @@ in }; }; updater = { - enable = mkEnableOption "freshclam updater"; + enable = mkEnableOption "ClamAV freshclam updater"; frequency = mkOption { + type = types.int; default = 12; description = '' Number of database checks per day. ''; }; - config = mkOption { + interval = mkOption { + type = types.str; + default = "hourly"; + description = '' + How often freshclam is invoked. See systemd.time(7) for more + information about the format. + ''; + }; + + extraConfig = mkOption { + type = types.lines; default = ""; description = '' Extra configuration for freshclam. Contents will be added verbatim to the @@ -68,50 +90,53 @@ in gid = config.ids.gids.clamav; }; - services.clamav.updater.config = mkIf cfg.updater.enable '' - DatabaseDirectory ${stateDir} - Foreground yes - Checks ${toString cfg.updater.frequency} - DatabaseMirror database.clamav.net - ''; + environment.etc."clamav/freshclam.conf".source = freshclamConfigFile; + environment.etc."clamav/clamd.conf".source = clamdConfigFile; - systemd.services.clamd = mkIf cfg.daemon.enable { + systemd.services.clamav-daemon = mkIf cfg.daemon.enable { description = "ClamAV daemon (clamd)"; - path = [ pkg ]; - after = [ "network.target" "freshclam.service" ]; - requires = [ "freshclam.service" ]; + after = mkIf cfg.updater.enable [ "clamav-freshclam.service" ]; + requires = mkIf cfg.updater.enable [ "clamav-freshclam.service" ]; wantedBy = [ "multi-user.target" ]; + restartTriggers = [ clamdConfigFile ]; + preStart = '' - mkdir -m 0755 -p ${logDir} mkdir -m 0755 -p ${runDir} - chown ${clamavUser}:${clamavGroup} ${logDir} chown ${clamavUser}:${clamavGroup} ${runDir} ''; + serviceConfig = { - ExecStart = "${pkg}/bin/clamd --config-file=${clamdConfigFile}"; - Type = "forking"; - ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; - Restart = "on-failure"; - RestartSec = "10s"; - StartLimitInterval = "1min"; + ExecStart = "${pkg}/bin/clamd"; + ExecReload = "${pkgs.coreutils}/bin/kill -USR2 $MAINPID"; + PrivateTmp = "yes"; + PrivateDevices = "yes"; + PrivateNetwork = "yes"; }; }; - systemd.services.freshclam = mkIf cfg.updater.enable { - description = "ClamAV updater (freshclam)"; - after = [ "network.target" ]; - wantedBy = [ "multi-user.target" ]; - path = [ pkg ]; + systemd.timers.clamav-freshclam = mkIf cfg.updater.enable { + description = "Timer for ClamAV virus database updater (freshclam)"; + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = cfg.updater.interval; + Unit = "clamav-freshclam.service"; + }; + }; + + systemd.services.clamav-freshclam = mkIf cfg.updater.enable { + description = "ClamAV virus database updater (freshclam)"; + restartTriggers = [ freshclamConfigFile ]; + preStart = '' mkdir -m 0755 -p ${stateDir} chown ${clamavUser}:${clamavGroup} ${stateDir} ''; + serviceConfig = { - ExecStart = "${pkg}/bin/freshclam --daemon --config-file=${pkgs.writeText "freshclam.conf" cfg.updater.config}"; - ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; - Restart = "on-failure"; - RestartSec = "10s"; - StartLimitInterval = "1min"; + Type = "oneshot"; + ExecStart = "${pkg}/bin/freshclam"; + PrivateTmp = "yes"; + PrivateDevices = "yes"; }; }; }; diff --git a/nixos/modules/services/security/fail2ban.nix b/nixos/modules/services/security/fail2ban.nix index 22e3bb0066c..716ae7a2d2f 100644 --- a/nixos/modules/services/security/fail2ban.nix +++ b/nixos/modules/services/security/fail2ban.nix @@ -143,7 +143,7 @@ in services.fail2ban.jails.ssh-iptables = '' filter = sshd - action = iptables[name=SSH, port=ssh, protocol=tcp] + action = iptables-multiport[name=SSH, port="${concatMapStringsSep "," (p: toString p) config.services.openssh.ports}", protocol=tcp] maxretry = 5 ''; diff --git a/nixos/modules/services/security/haveged.nix b/nixos/modules/services/security/haveged.nix index 2aa523bf70a..eca52918881 100644 --- a/nixos/modules/services/security/haveged.nix +++ b/nixos/modules/services/security/haveged.nix @@ -48,14 +48,18 @@ in { description = "Entropy Harvesting Daemon"; unitConfig.Documentation = "man:haveged(8)"; wantedBy = [ "multi-user.target" ]; - + path = [ pkgs.haveged ]; - - serviceConfig = - { Type = "forking"; - ExecStart = "${pkgs.haveged}/sbin/haveged -w ${toString cfg.refill_threshold} -v 1"; - PIDFile = "/run/haveged.pid"; - }; + + serviceConfig = { + ExecStart = "${pkgs.haveged}/bin/haveged -F -w ${toString cfg.refill_threshold} -v 1"; + SuccessExitStatus = 143; + PrivateTmp = true; + PrivateDevices = true; + PrivateNetwork = true; + ProtectSystem = "full"; + ProtectHome = true; + }; }; }; diff --git a/nixos/modules/services/security/oauth2_proxy.nix b/nixos/modules/services/security/oauth2_proxy.nix index 4c20392214f..caa7d9d5081 100644 --- a/nixos/modules/services/security/oauth2_proxy.nix +++ b/nixos/modules/services/security/oauth2_proxy.nix @@ -510,7 +510,7 @@ in description = "OAuth2 Proxy"; path = [ cfg.package ]; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; serviceConfig = { User = "oauth2_proxy"; diff --git a/nixos/modules/services/system/cgmanager.nix b/nixos/modules/services/system/cgmanager.nix new file mode 100644 index 00000000000..59d3deced86 --- /dev/null +++ b/nixos/modules/services/system/cgmanager.nix @@ -0,0 +1,27 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.cgmanager; +in { + meta.maintainers = [ maintainers.mic92 ]; + + ###### interface + options.services.cgmanager.enable = mkEnableOption "cgmanager"; + + ###### implementation + config = mkIf cfg.enable { + systemd.services.cgmanager = { + wantedBy = [ "multi-user.target" ]; + after = [ "local-fs.target" ]; + description = "Cgroup management daemon"; + restartIfChanged = false; + serviceConfig = { + ExecStart = "${pkgs.cgmanager}/bin/cgmanager -m name=systemd"; + KillMode = "process"; + Restart = "on-failure"; + }; + }; + }; +} diff --git a/nixos/modules/services/system/dbus.nix b/nixos/modules/services/system/dbus.nix index df93fcd2bdb..d15d5551e34 100644 --- a/nixos/modules/services/system/dbus.nix +++ b/nixos/modules/services/system/dbus.nix @@ -8,7 +8,7 @@ let cfg = config.services.dbus; - homeDir = "/var/run/dbus"; + homeDir = "/run/dbus"; systemExtraxml = concatStrings (flip concatMap cfg.packages (d: [ "<servicedir>${d}/share/dbus-1/system-services</servicedir>" @@ -20,15 +20,23 @@ let "<includedir>${d}/etc/dbus-1/session.d</includedir>" ])); - configDir = pkgs.stdenv.mkDerivation { - name = "dbus-conf"; + daemonArgs = "--address=systemd: --nofork --nopidfile --systemd-activation"; - preferLocalBuild = true; - allowSubstitutes = false; - - buildCommand = '' + configDir = pkgs.runCommand "dbus-conf" + { preferLocalBuild = true; + allowSubstitutes = false; + } + '' mkdir -p $out + cp ${pkgs.dbus.out}/share/dbus-1/{system,session}.conf $out + + # avoid circular includes + sed -ri 's@(<include ignore_missing="yes">/etc/dbus-1/(system|session)\.conf</include>)@<!-- \1 -->@g' $out/{system,session}.conf + + # include by full path + sed -ri "s@/etc/dbus-1/(system|session)-@$out/\1-@" $out/{system,session}.conf + sed '${./dbus-system-local.conf.in}' \ -e 's,@servicehelper@,${config.security.permissionsWrapperDir}/dbus-daemon-launch-helper,g' \ -e 's,@extra@,${systemExtraxml},' \ @@ -38,7 +46,6 @@ let -e 's,@extra@,${sessionExtraxml},' \ > "$out/session-local.conf" ''; - }; in @@ -75,16 +82,21 @@ in ''; }; + socketActivated = mkOption { + type = types.bool; + default = false; + description = '' + Make the user instance socket activated. + ''; + }; }; - }; - ###### implementation config = mkIf cfg.enable { - environment.systemPackages = [ pkgs.dbus.daemon pkgs.dbus_tools ]; + environment.systemPackages = [ pkgs.dbus.daemon pkgs.dbus ]; environment.etc = singleton { source = configDir; @@ -104,7 +116,7 @@ in security.permissionsWrappers.setuid = singleton { program = "dbus-daemon-launch-helper"; - source = "${pkgs.dbus_daemon}/libexec/dbus-daemon-launch-helper"; + source = "${pkgs.dbus.daemon}/libexec/dbus-daemon-launch-helper"; owner = "root"; group = "messagebus"; setuid = true; @@ -117,13 +129,29 @@ in config.system.path ]; - # Don't restart dbus-daemon. Bad things tend to happen if we do. - systemd.services.dbus.reloadIfChanged = true; + systemd.services.dbus = { + # Don't restart dbus-daemon. Bad things tend to happen if we do. + reloadIfChanged = true; + restartTriggers = [ configDir ]; + serviceConfig.ExecStart = [ + "" + "${lib.getBin pkgs.dbus}/bin/dbus-daemon --config-file=${configDir}/system.conf ${daemonArgs}" + ]; + }; - systemd.services.dbus.restartTriggers = [ configDir ]; + systemd.user = { + services.dbus = { + # Don't restart dbus-daemon. Bad things tend to happen if we do. + reloadIfChanged = true; + restartTriggers = [ configDir ]; + serviceConfig.ExecStart = [ + "" + "${lib.getBin pkgs.dbus}/bin/dbus-daemon --config-file=${configDir}/session.conf ${daemonArgs}" + ]; + }; + sockets.dbus.wantedBy = mkIf cfg.socketActivated [ "sockets.target" ]; + }; environment.pathsToLink = [ "/etc/dbus-1" "/share/dbus-1" ]; - }; - } diff --git a/nixos/modules/services/system/nscd.nix b/nixos/modules/services/system/nscd.nix index d98ef8a306d..eb4b5281c7c 100644 --- a/nixos/modules/services/system/nscd.nix +++ b/nixos/modules/services/system/nscd.nix @@ -9,8 +9,6 @@ let inherit (lib) singleton; - cfgFile = pkgs.writeText "nscd.conf" cfg.config; - in { @@ -41,6 +39,7 @@ in ###### implementation config = mkIf cfg.enable { + environment.etc."nscd.conf".text = cfg.config; users.extraUsers.nscd = { isSystemUser = true; @@ -61,10 +60,14 @@ in mkdir -m 0755 -p /var/db/nscd ''; - restartTriggers = [ config.environment.etc.hosts.source config.environment.etc."nsswitch.conf".source ]; + restartTriggers = [ + config.environment.etc.hosts.source + config.environment.etc."nsswitch.conf".source + config.environment.etc."nscd.conf".source + ]; serviceConfig = - { ExecStart = "@${pkgs.glibc.bin}/sbin/nscd nscd -f ${cfgFile}"; + { ExecStart = "@${pkgs.glibc.bin}/sbin/nscd nscd"; Type = "forking"; PIDFile = "/run/nscd/nscd.pid"; Restart = "always"; @@ -79,7 +82,7 @@ in # its pid. So wait until it's ready. postStart = '' - while ! ${pkgs.glibc.bin}/sbin/nscd -g -f ${cfgFile} > /dev/null; do + while ! ${pkgs.glibc.bin}/sbin/nscd -g > /dev/null; do sleep 0.2 done ''; diff --git a/nixos/modules/services/torrent/deluge.nix b/nixos/modules/services/torrent/deluge.nix index becd57055d4..a9c08b66eb8 100644 --- a/nixos/modules/services/torrent/deluge.nix +++ b/nixos/modules/services/torrent/deluge.nix @@ -5,26 +5,36 @@ with lib; let cfg = config.services.deluge; cfg_web = config.services.deluge.web; + openFilesLimit = 4096; + in { options = { - services.deluge = { - enable = mkOption { - default = false; - example = true; - description = '' - Start Deluge daemon. - ''; - }; - }; + services = { + deluge = { + enable = mkOption { + default = false; + example = true; + description = "Start the Deluge daemon"; + }; + + openFilesLimit = mkOption { + default = openFilesLimit; + example = 8192; + description = '' + Number of files to allow deluged to open. + ''; + }; + }; - services.deluge.web = { - enable = mkOption { - default = false; - example = true; - description = '' - Start Deluge Web daemon. - ''; - }; + deluge.web = { + enable = mkOption { + default = false; + example = true; + description = '' + Start Deluge Web daemon. + ''; + }; + }; }; }; @@ -35,11 +45,14 @@ in { description = "Deluge BitTorrent Daemon"; wantedBy = [ "multi-user.target" ]; path = [ pkgs.pythonPackages.deluge ]; - serviceConfig.ExecStart = "${pkgs.pythonPackages.deluge}/bin/deluged -d"; - # To prevent "Quit & shutdown daemon" from working; we want systemd to manage it! - serviceConfig.Restart = "on-success"; - serviceConfig.User = "deluge"; - serviceConfig.Group = "deluge"; + serviceConfig = { + ExecStart = "${pkgs.pythonPackages.deluge}/bin/deluged -d"; + # To prevent "Quit & shutdown daemon" from working; we want systemd to manage it! + Restart = "on-success"; + User = "deluge"; + Group = "deluge"; + LimitNOFILE = cfg.openFilesLimit; + }; }; systemd.services.delugeweb = mkIf cfg_web.enable { diff --git a/nixos/modules/services/torrent/flexget.nix b/nixos/modules/services/torrent/flexget.nix index 1252aa1c549..4b9038e3e25 100644 --- a/nixos/modules/services/torrent/flexget.nix +++ b/nixos/modules/services/torrent/flexget.nix @@ -4,7 +4,7 @@ with lib; let cfg = config.services.flexget; - pkg = pkgs.python27Packages.flexget; + pkg = pkgs.flexget; ymlFile = pkgs.writeText "flexget.yml" '' ${cfg.config} @@ -54,12 +54,12 @@ in { config = mkIf cfg.enable { - environment.systemPackages = [ pkgs.python27Packages.flexget ]; + environment.systemPackages = [ pkg ]; systemd.services = { flexget = { description = "FlexGet Daemon"; - path = [ pkgs.pythonPackages.flexget ]; + path = [ pkg ]; serviceConfig = { User = cfg.user; Environment = "TZ=${config.time.timeZone}"; diff --git a/nixos/modules/services/torrent/opentracker.nix b/nixos/modules/services/torrent/opentracker.nix new file mode 100644 index 00000000000..74f443381d9 --- /dev/null +++ b/nixos/modules/services/torrent/opentracker.nix @@ -0,0 +1,45 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.services.opentracker; +in { + options.services.opentracker = { + enable = mkEnableOption "opentracker"; + + package = mkOption { + type = types.package; + description = '' + opentracker package to use + ''; + default = pkgs.opentracker; + defaultText = "pkgs.opentracker"; + }; + + extraOptions = mkOption { + type = types.separatedString " "; + description = '' + Configuration Arguments for opentracker + See https://erdgeist.org/arts/software/opentracker/ for all params + ''; + default = ""; + }; + }; + + config = lib.mkIf cfg.enable { + + systemd.services.opentracker = { + description = "opentracker server"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + restartIfChanged = true; + serviceConfig = { + ExecStart = "${cfg.package}/bin/opentracker ${cfg.extraOptions}"; + PrivateTmp = true; + WorkingDirectory = "/var/empty"; + # By default opentracker drops all privileges and runs in chroot after starting up as root. + }; + }; + }; +} + diff --git a/nixos/modules/services/torrent/peerflix.nix b/nixos/modules/services/torrent/peerflix.nix index 38fbd3b226c..2e3dd9902d7 100644 --- a/nixos/modules/services/torrent/peerflix.nix +++ b/nixos/modules/services/torrent/peerflix.nix @@ -42,7 +42,7 @@ in { systemd.services.peerflix = { description = "Peerflix Daemon"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; environment.HOME = cfg.stateDir; preStart = '' diff --git a/nixos/modules/services/ttys/agetty.nix b/nixos/modules/services/ttys/agetty.nix index ea7196fc873..051d54e932f 100644 --- a/nixos/modules/services/ttys/agetty.nix +++ b/nixos/modules/services/ttys/agetty.nix @@ -80,8 +80,7 @@ in }; systemd.services."container-getty@" = - { unitConfig.ConditionPathExists = "/dev/pts/%I"; # Work around being respawned when "machinectl login" exits. - serviceConfig.ExecStart = gettyCmd "--noclear --keep-baud pts/%I 115200,38400,9600 $TERM"; + { serviceConfig.ExecStart = gettyCmd "--noclear --keep-baud pts/%I 115200,38400,9600 $TERM"; restartIfChanged = false; }; diff --git a/nixos/modules/services/web-apps/atlassian/confluence.nix b/nixos/modules/services/web-apps/atlassian/confluence.nix new file mode 100644 index 00000000000..2d9287577de --- /dev/null +++ b/nixos/modules/services/web-apps/atlassian/confluence.nix @@ -0,0 +1,141 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.confluence; + + pkg = pkgs.atlassian-confluence; + +in + +{ + options = { + services.confluence = { + enable = mkEnableOption "Atlassian Confluence service"; + + user = mkOption { + type = types.str; + default = "confluence"; + description = "User which runs confluence."; + }; + + group = mkOption { + type = types.str; + default = "confluence"; + description = "Group which runs confluence."; + }; + + home = mkOption { + type = types.str; + default = "/var/lib/confluence"; + description = "Home directory of the confluence instance."; + }; + + listenAddress = mkOption { + type = types.str; + default = "127.0.0.1"; + description = "Address to listen on."; + }; + + listenPort = mkOption { + type = types.int; + default = 8090; + description = "Port to listen on."; + }; + + catalinaOptions = mkOption { + type = types.listOf types.str; + default = []; + example = [ "-Xms1024m" "-Xmx2048m" "-Dconfluence.disable.peopledirectory.all=true" ]; + description = "Java options to pass to catalina/tomcat."; + }; + + proxy = { + enable = mkEnableOption "proxy support"; + + name = mkOption { + type = types.str; + example = "confluence.example.com"; + description = "Virtual hostname at the proxy"; + }; + + port = mkOption { + type = types.int; + default = 443; + example = 80; + description = "Port used at the proxy"; + }; + + scheme = mkOption { + type = types.str; + default = "https"; + example = "http"; + description = "Protocol used at the proxy."; + }; + }; + + jrePackage = let + jreSwitch = unfree: free: if config.nixpkgs.config.allowUnfree or false then unfree else free; + in mkOption { + type = types.package; + default = jreSwitch pkgs.oraclejre8 pkgs.openjdk8.jre; + defaultText = jreSwitch "pkgs.oraclejre8" "pkgs.openjdk8.jre"; + example = literalExample "pkgs.openjdk8.jre"; + description = "Java Runtime to use for Confluence. Note that Atlassian recommends the Oracle JRE."; + }; + }; + }; + + config = mkIf cfg.enable { + users.extraUsers."${cfg.user}" = { + isSystemUser = true; + group = cfg.group; + }; + + users.extraGroups."${cfg.group}" = {}; + + systemd.services.confluence = { + description = "Atlassian Confluence"; + + wantedBy = [ "multi-user.target" ]; + requires = [ "postgresql.service" ]; + after = [ "postgresql.service" ]; + + path = [ cfg.jrePackage ]; + + environment = { + CONF_USER = cfg.user; + JAVA_HOME = "${cfg.jrePackage}"; + CATALINA_OPTS = concatStringsSep " " cfg.catalinaOptions; + }; + + preStart = '' + mkdir -p ${cfg.home}/{logs,work,temp,deploy} + + mkdir -p /run/confluence + ln -sf ${cfg.home}/{logs,work,temp,server.xml} /run/confluence + ln -sf ${cfg.home} /run/confluence/home + + chown -R ${cfg.user} ${cfg.home} + + sed -e 's,port="8090",port="${toString cfg.listenPort}" address="${cfg.listenAddress}",' \ + '' + (lib.optionalString cfg.proxy.enable '' + -e 's,protocol="org.apache.coyote.http11.Http11NioProtocol",protocol="org.apache.coyote.http11.Http11NioProtocol" proxyName="${cfg.proxy.name}" proxyPort="${toString cfg.proxy.port}" scheme="${cfg.proxy.scheme}",' \ + '') + '' + ${pkg}/conf/server.xml.dist > ${cfg.home}/server.xml + ''; + + script = "${pkg}/bin/start-confluence.sh -fg"; + stopScript = "${pkg}/bin/stop-confluence.sh"; + + serviceConfig = { + User = cfg.user; + Group = cfg.group; + PrivateTmp = true; + PermissionsStartOnly = true; + }; + }; + }; +} diff --git a/nixos/modules/services/web-apps/atlassian/crowd.nix b/nixos/modules/services/web-apps/atlassian/crowd.nix new file mode 100644 index 00000000000..ada26f8057b --- /dev/null +++ b/nixos/modules/services/web-apps/atlassian/crowd.nix @@ -0,0 +1,156 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.crowd; + + pkg = pkgs.atlassian-crowd.override { + home = cfg.home; + port = cfg.listenPort; + proxyUrl = "${cfg.proxy.scheme}://${cfg.proxy.name}:${toString cfg.proxy.port}"; + openidPassword = cfg.openidPassword; + }; + +in + +{ + options = { + services.crowd = { + enable = mkEnableOption "Atlassian Crowd service"; + + user = mkOption { + type = types.str; + default = "crowd"; + description = "User which runs Crowd."; + }; + + group = mkOption { + type = types.str; + default = "crowd"; + description = "Group which runs Crowd."; + }; + + home = mkOption { + type = types.str; + default = "/var/lib/crowd"; + description = "Home directory of the Crowd instance."; + }; + + listenAddress = mkOption { + type = types.str; + default = "127.0.0.1"; + description = "Address to listen on."; + }; + + listenPort = mkOption { + type = types.int; + default = 8092; + description = "Port to listen on."; + }; + + openidPassword = mkOption { + type = types.str; + description = "Application password for OpenID server."; + }; + + catalinaOptions = mkOption { + type = types.listOf types.str; + default = []; + example = [ "-Xms1024m" "-Xmx2048m" ]; + description = "Java options to pass to catalina/tomcat."; + }; + + proxy = { + enable = mkEnableOption "reverse proxy support"; + + name = mkOption { + type = types.str; + example = "crowd.example.com"; + description = "Virtual hostname at the proxy"; + }; + + port = mkOption { + type = types.int; + default = 443; + example = 80; + description = "Port used at the proxy"; + }; + + scheme = mkOption { + type = types.str; + default = "https"; + example = "http"; + description = "Protocol used at the proxy."; + }; + + secure = mkOption { + type = types.bool; + default = true; + example = false; + description = "Whether the connections to the proxy should be considered secure."; + }; + }; + + jrePackage = let + jreSwitch = unfree: free: if config.nixpkgs.config.allowUnfree or false then unfree else free; + in mkOption { + type = types.package; + default = jreSwitch pkgs.oraclejre8 pkgs.openjdk8.jre; + defaultText = jreSwitch "pkgs.oraclejre8" "pkgs.openjdk8.jre"; + example = literalExample "pkgs.openjdk8.jre"; + description = "Java Runtime to use for Crowd. Note that Atlassian recommends the Oracle JRE."; + }; + }; + }; + + config = mkIf cfg.enable { + users.extraUsers."${cfg.user}" = { + isSystemUser = true; + group = cfg.group; + }; + + users.extraGroups."${cfg.group}" = {}; + + systemd.services.atlassian-crowd = { + description = "Atlassian Crowd"; + + wantedBy = [ "multi-user.target" ]; + requires = [ "postgresql.service" ]; + after = [ "postgresql.service" ]; + + path = [ cfg.jrePackage ]; + + environment = { + JAVA_HOME = "${cfg.jrePackage}"; + CATALINA_OPTS = concatStringsSep " " cfg.catalinaOptions; + CATALINA_TMPDIR = "/tmp"; + }; + + preStart = '' + mkdir -p ${cfg.home}/{logs,work,database} + + mkdir -p /run/atlassian-crowd + ln -sf ${cfg.home}/{database,work,server.xml} /run/atlassian-crowd + + chown -R ${cfg.user} ${cfg.home} + + sed -e 's,port="8095",port="${toString cfg.listenPort}" address="${cfg.listenAddress}",' \ + '' + (lib.optionalString cfg.proxy.enable '' + -e 's,compression="on",compression="off" protocol="HTTP/1.1" proxyName="${cfg.proxy.name}" proxyPort="${toString cfg.proxy.port}" scheme="${cfg.proxy.scheme}" secure="${toString cfg.proxy.secure}",' \ + '') + '' + ${pkg}/apache-tomcat/conf/server.xml.dist > ${cfg.home}/server.xml + ''; + + script = "${pkg}/start_crowd.sh"; + + serviceConfig = { + User = cfg.user; + Group = cfg.group; + PrivateTmp = true; + PermissionsStartOnly = true; + }; + }; + }; +} diff --git a/nixos/modules/services/web-apps/atlassian/jira.nix b/nixos/modules/services/web-apps/atlassian/jira.nix new file mode 100644 index 00000000000..6e31d20d068 --- /dev/null +++ b/nixos/modules/services/web-apps/atlassian/jira.nix @@ -0,0 +1,149 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.jira; + + pkg = pkgs.atlassian-jira; + +in + +{ + options = { + services.jira = { + enable = mkEnableOption "Atlassian JIRA service"; + + user = mkOption { + type = types.str; + default = "jira"; + description = "User which runs JIRA."; + }; + + group = mkOption { + type = types.str; + default = "jira"; + description = "Group which runs JIRA."; + }; + + home = mkOption { + type = types.str; + default = "/var/lib/jira"; + description = "Home directory of the JIRA instance."; + }; + + listenAddress = mkOption { + type = types.str; + default = "127.0.0.1"; + description = "Address to listen on."; + }; + + listenPort = mkOption { + type = types.int; + default = 8091; + description = "Port to listen on."; + }; + + catalinaOptions = mkOption { + type = types.listOf types.str; + default = []; + example = [ "-Xms1024m" "-Xmx2048m" ]; + description = "Java options to pass to catalina/tomcat."; + }; + + proxy = { + enable = mkEnableOption "reverse proxy support"; + + name = mkOption { + type = types.str; + example = "jira.example.com"; + description = "Virtual hostname at the proxy"; + }; + + port = mkOption { + type = types.int; + default = 443; + example = 80; + description = "Port used at the proxy"; + }; + + scheme = mkOption { + type = types.str; + default = "https"; + example = "http"; + description = "Protocol used at the proxy."; + }; + + secure = mkOption { + type = types.bool; + default = true; + example = false; + description = "Whether the connections to the proxy should be considered secure."; + }; + }; + + jrePackage = let + jreSwitch = unfree: free: if config.nixpkgs.config.allowUnfree or false then unfree else free; + in mkOption { + type = types.package; + default = jreSwitch pkgs.oraclejre8 pkgs.openjdk8.jre; + defaultText = jreSwitch "pkgs.oraclejre8" "pkgs.openjdk8.jre"; + example = literalExample "pkgs.openjdk8.jre"; + description = "Java Runtime to use for JIRA. Note that Atlassian recommends the Oracle JRE."; + }; + }; + }; + + config = mkIf cfg.enable { + users.extraUsers."${cfg.user}" = { + isSystemUser = true; + group = cfg.group; + }; + + users.extraGroups."${cfg.group}" = {}; + + systemd.services.atlassian-jira = { + description = "Atlassian JIRA"; + + wantedBy = [ "multi-user.target" ]; + requires = [ "postgresql.service" ]; + after = [ "postgresql.service" ]; + + path = [ cfg.jrePackage ]; + + environment = { + JIRA_USER = cfg.user; + JIRA_HOME = cfg.home; + JAVA_HOME = "${cfg.jrePackage}"; + CATALINA_OPTS = concatStringsSep " " cfg.catalinaOptions; + }; + + preStart = '' + mkdir -p ${cfg.home}/{logs,work,temp,deploy} + + mkdir -p /run/atlassian-jira + ln -sf ${cfg.home}/{logs,work,temp,server.xml} /run/atlassian-jira + ln -sf ${cfg.home} /run/atlassian-jira/home + + chown -R ${cfg.user} ${cfg.home} + + sed -e 's,port="8080",port="${toString cfg.listenPort}" address="${cfg.listenAddress}",' \ + '' + (lib.optionalString cfg.proxy.enable '' + -e 's,protocol="HTTP/1.1",protocol="HTTP/1.1" proxyName="${cfg.proxy.name}" proxyPort="${toString cfg.proxy.port}" scheme="${cfg.proxy.scheme}" secure="${toString cfg.proxy.secure}",' \ + '') + '' + ${pkg}/conf/server.xml.dist > ${cfg.home}/server.xml + ''; + + script = "${pkg}/bin/start-jira.sh -fg"; + stopScript = "${pkg}/bin/stop-jira.sh"; + + serviceConfig = { + User = cfg.user; + Group = cfg.group; + PrivateTmp = true; + PermissionsStartOnly = true; + }; + }; + }; +} diff --git a/nixos/modules/services/web-apps/nixbot.nix b/nixos/modules/services/web-apps/nixbot.nix new file mode 100644 index 00000000000..0592d01bf36 --- /dev/null +++ b/nixos/modules/services/web-apps/nixbot.nix @@ -0,0 +1,149 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.nixbot; + pyramidIni = '' + ### + # app configuration + # http://docs.pylonsproject.org/projects/pyramid/en/1.7-branch/narr/environment.html + ### + + [app:main] + use = egg:nixbot + + nixbot.github_token = ${cfg.githubToken} + nixbot.bot_name = ${cfg.botName} + nixbot.repo = ${cfg.repo} + nixbot.pr_repo = ${cfg.prRepo} + nixbot.hydra_jobsets_repo = ${cfg.hydraJobsetsRepo} + nixbot.github_secret = justnotsorandom + nixbot.public_url = ${cfg.publicUrl} + nixbot.repo_dir = ${cfg.repoDir} + + pyramid.reload_templates = false + pyramid.debug_authorization = false + pyramid.debug_notfound = false + pyramid.debug_routematch = false + pyramid.default_locale_name = en + + # By default, the toolbar only appears for clients from IP addresses + # '127.0.0.1' and '::1'. + # debugtoolbar.hosts = 127.0.0.1 ::1 + + ### + # wsgi server configuration + ### + + [server:main] + use = egg:waitress#main + host = 0.0.0.0 + port = 6543 + + ### + # logging configuration + # http://docs.pylonsproject.org/projects/pyramid/en/1.7-branch/narr/logging.html + ### + + [loggers] + keys = root, nixbot + + [handlers] + keys = console + + [formatters] + keys = generic + + [logger_root] + level = INFO + handlers = console + + [logger_nixbot] + level = INFO + handlers = + qualname = nixbot + + [handler_console] + class = StreamHandler + args = (sys.stderr,) + level = NOTSET + formatter = generic + + [formatter_generic] + format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s + ''; +in { + options = { + services.nixbot = { + enable = mkEnableOption "nixbot"; + + botName = mkOption { + type = types.str; + description = "The bot's github user account name."; + default = "nixbot"; + }; + + githubToken = mkOption { + type = types.str; + description = "The bot's github user account token."; + example = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + }; + + repo = mkOption { + type = types.str; + description = "The github repository to check for PRs."; + example = "nixos/nixpkgs"; + }; + + prRepo = mkOption { + type = types.str; + description = "The github repository to push the testing branches to."; + example = "nixos/nixpkgs-pr"; + }; + + hydraJobsetsRepo = mkOption { + type = types.str; + description = "The github repository to push the hydra jobset definitions to."; + example = "nixos/hydra-jobsets"; + }; + + publicUrl = mkOption { + type = types.str; + description = "The public URL the bot is reachable at (Github hook endpoint)."; + example = "https://nixbot.nixos.org"; + }; + + repoDir = mkOption { + type = types.path; + description = "The directory the repositories are stored in."; + default = "/var/lib/nixbot"; + }; + }; + }; + + config = mkIf cfg.enable { + users.extraUsers.nixbot = { + createHome = true; + home = cfg.repoDir; + }; + + systemd.services.nixbot = let + env = pkgs.python3.buildEnv.override { + extraLibs = [ pkgs.nixbot ]; + }; + in { + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + script = '' + ${env}/bin/pserve ${pkgs.writeText "production.ini" pyramidIni} + ''; + + serviceConfig = { + User = "nixbot"; + Group = "nogroup"; + PermissionsStartOnly = true; + }; + }; + }; +} diff --git a/nixos/modules/services/web-apps/quassel-webserver.nix b/nixos/modules/services/web-apps/quassel-webserver.nix new file mode 100644 index 00000000000..d19e4bc5827 --- /dev/null +++ b/nixos/modules/services/web-apps/quassel-webserver.nix @@ -0,0 +1,101 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.quassel-webserver; + quassel-webserver = cfg.pkg; + settings = '' + module.exports = { + default: { + host: '${cfg.quasselCoreHost}', // quasselcore host + port: ${toString cfg.quasselCorePort}, // quasselcore port + initialBacklogLimit: ${toString cfg.initialBacklogLimit}, // Amount of backlogs to fetch per buffer on connection + backlogLimit: ${toString cfg.backlogLimit}, // Amount of backlogs to fetch per buffer after first retrieval + securecore: ${if cfg.secureCore then "true" else "false"}, // Connect to the core using SSL + theme: '${cfg.theme}' // Default UI theme + }, + themes: ['default', 'darksolarized'], // Available themes + forcedefault: ${if cfg.forceHostAndPort then "true" else "false"}, // Will force default host and port to be used, and will hide the corresponding fields in the UI + prefixpath: '${cfg.prefixPath}' // Configure this if you use a reverse proxy + }; + ''; + settingsFile = pkgs.writeText "settings-user.js" settings; +in { + options = { + services.quassel-webserver = { + enable = mkOption { + default = false; + type = types.bool; + description = "Whether to enable the quassel webclient service"; + }; + pkg = mkOption { + default = pkgs.quassel-webserver; + defaultText = "pkgs.quassel-webserver"; + type = types.package; + description = "The quassel-webserver package"; + }; + quasselCoreHost = mkOption { + default = ""; + type = types.str; + description = "The default host of the quassel core"; + }; + quasselCorePort = mkOption { + default = 4242; + type = types.int; + description = "The default quassel core port"; + }; + initialBacklogLimit = mkOption { + default = 20; + type = types.int; + description = "Amount of backlogs to fetch per buffer on connection"; + }; + backlogLimit = mkOption { + default = 100; + type = types.int; + description = "Amount of backlogs to fetch per buffer after first retrieval"; + }; + secureCore = mkOption { + default = true; + type = types.bool; + description = "Connect to the core using SSL"; + }; + theme = mkOption { + default = "default"; + type = types.str; + description = "default or darksolarized"; + }; + prefixPath = mkOption { + default = ""; + type = types.str; + description = "Configure this if you use a reverse proxy. Must start with a '/'"; + example = "/quassel"; + }; + port = mkOption { + default = 60443; + type = types.int; + description = "The port the quassel webserver should listen on"; + }; + useHttps = mkOption { + default = true; + type = types.bool; + description = "Whether the quassel webserver connection should be a https connection"; + }; + forceHostAndPort = mkOption { + default = false; + type = types.bool; + description = "Force the users to use the quasselCoreHost and quasselCorePort defaults"; + }; + }; + }; + + config = mkIf cfg.enable { + systemd.services.quassel-webserver = { + description = "A web server/client for Quassel"; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStart = "${quassel-webserver}/lib/node_modules/quassel-webserver/bin/www -p ${toString cfg.port} -m ${if cfg.useHttps == true then "https" else "http"} -c ${settingsFile}"; + }; + }; + }; +} diff --git a/nixos/modules/services/web-apps/selfoss.nix b/nixos/modules/services/web-apps/selfoss.nix new file mode 100644 index 00000000000..5571f77334c --- /dev/null +++ b/nixos/modules/services/web-apps/selfoss.nix @@ -0,0 +1,166 @@ +{ config, lib, pkgs, ... }: +with lib; +let + cfg = config.services.selfoss; + + poolName = "selfoss_pool"; + phpfpmSocketName = "/var/run/phpfpm/${poolName}.sock"; + + dataDir = "/var/lib/selfoss"; + + selfoss-config = + let + db_type = cfg.database.type; + default_port = if (db_type == "mysql") then 3306 else 5342; + in + pkgs.writeText "selfoss-config.ini" '' + [globals] + ${lib.optionalString (db_type != "sqlite") '' + db_type=${db_type} + db_host=${cfg.database.host} + db_database=${cfg.database.name} + db_username=${cfg.database.user} + db_password=${cfg.database.password} + db_port=${if (cfg.database.port != null) then cfg.database.port + else default_port} + '' + } + ${cfg.extraConfig} + ''; +in + { + options = { + services.selfoss = { + enable = mkEnableOption "selfoss"; + + user = mkOption { + type = types.str; + default = "nginx"; + example = "nginx"; + description = '' + User account under which both the service and the web-application run. + ''; + }; + + pool = mkOption { + type = types.str; + default = "${poolName}"; + description = '' + Name of existing phpfpm pool that is used to run web-application. + If not specified a pool will be created automatically with + default values. + ''; + }; + + database = { + type = mkOption { + type = types.enum ["pgsql" "mysql" "sqlite"]; + default = "sqlite"; + description = '' + Database to store feeds. Supported are sqlite, pgsql and mysql. + ''; + }; + + host = mkOption { + type = types.str; + default = "localhost"; + description = '' + Host of the database (has no effect if type is "sqlite"). + ''; + }; + + name = mkOption { + type = types.str; + default = "tt_rss"; + description = '' + Name of the existing database (has no effect if type is "sqlite"). + ''; + }; + + user = mkOption { + type = types.str; + default = "tt_rss"; + description = '' + The database user. The user must exist and has access to + the specified database (has no effect if type is "sqlite"). + ''; + }; + + password = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + The database user's password (has no effect if type is "sqlite"). + ''; + }; + + port = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + The database's port. If not set, the default ports will be + provided (5432 and 3306 for pgsql and mysql respectively) + (has no effect if type is "sqlite"). + ''; + }; + }; + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra configuration added to config.ini + ''; + }; + }; + }; + + config = mkIf cfg.enable { + + services.phpfpm.poolConfigs = mkIf (cfg.pool == "${poolName}") { + "${poolName}" = '' + listen = "${phpfpmSocketName}"; + listen.owner = nginx + listen.group = nginx + listen.mode = 0600 + user = nginx + pm = dynamic + pm.max_children = 75 + pm.start_servers = 10 + pm.min_spare_servers = 5 + pm.max_spare_servers = 20 + pm.max_requests = 500 + catch_workers_output = 1 + ''; + }; + + systemd.services.selfoss-config = { + serviceConfig.Type = "oneshot"; + script = '' + mkdir -m 755 -p ${dataDir} + cd ${dataDir} + + # Delete all but the "data" folder + ls | grep -v data | while read line; do rm -rf $line; done || true + + # Create the files + cp -r "${pkgs.selfoss}/"* "${dataDir}" + ln -sf "${selfoss-config}" "${dataDir}/config.ini" + chown -R "${cfg.user}" "${dataDir}" + chmod -R 755 "${dataDir}" + ''; + wantedBy = [ "multi-user.target" ]; + }; + + systemd.services.selfoss-update = { + serviceConfig = { + ExecStart = "${pkgs.php}/bin/php ${dataDir}/cliupdate.php"; + User = "${cfg.user}"; + }; + startAt = "hourly"; + after = [ "selfoss-config.service" ]; + wantedBy = [ "multi-user.target" ]; + + }; + + }; +} diff --git a/nixos/modules/services/web-apps/tt-rss.nix b/nixos/modules/services/web-apps/tt-rss.nix index b08070f1e36..5193814da72 100644 --- a/nixos/modules/services/web-apps/tt-rss.nix +++ b/nixos/modules/services/web-apps/tt-rss.nix @@ -18,7 +18,6 @@ let poolName = "tt-rss"; phpfpmSocketName = "/var/run/phpfpm/${poolName}.sock"; - virtualHostName = "tt-rss"; tt-rss-config = pkgs.writeText "config.php" '' <?php @@ -34,10 +33,10 @@ let define('MYSQL_CHARSET', 'UTF8'); define('DB_TYPE', '${cfg.database.type}'); - define('DB_HOST', '${cfg.database.host}'); + define('DB_HOST', '${optionalString (cfg.database.host != null) cfg.database.host}'); define('DB_USER', '${cfg.database.user}'); define('DB_NAME', '${cfg.database.name}'); - define('DB_PASS', '${escape ["'" "\\"] cfg.database.password}'); + define('DB_PASS', '${optionalString (cfg.database.password != null) (escape ["'" "\\"] cfg.database.password)}'); define('DB_PORT', '${toString dbPort}'); define('AUTH_AUTO_CREATE', ${boolToString cfg.auth.autoCreate}); @@ -91,12 +90,21 @@ let enable = mkEnableOption "tt-rss"; + root = mkOption { + type = types.path; + default = "/var/lib/tt-rss"; + example = "/var/lib/tt-rss"; + description = '' + Root of the application. + ''; + }; + user = mkOption { type = types.str; default = "nginx"; example = "nginx"; description = '' - User account under which both the service and the web-application run. + User account under which both the update daemon and the web-application run. ''; }; @@ -110,17 +118,13 @@ let ''; }; - # TODO: Re-enable after https://github.com/NixOS/nixpkgs/pull/15862 is merged - - # virtualHost = mkOption { - # type = types.str; - # default = "${virtualHostName}"; - # description = '' - # Name of existing nginx virtual host that is used to run web-application. - # If not specified a host will be created automatically with - # default values. - # ''; - # }; + virtualHost = mkOption { + type = types.nullOr types.str; + default = "tt-rss"; + description = '' + Name of the nginx virtualhost to use and setup. If null, do not setup any virtualhost. + ''; + }; database = { type = mkOption { @@ -132,10 +136,10 @@ let }; host = mkOption { - type = types.str; - default = "localhost"; + type = types.nullOr types.str; + default = null; description = '' - Host of the database. + Host of the database. Leave null to use Unix domain socket. ''; }; @@ -362,7 +366,7 @@ let singleUserMode = mkOption { type = types.bool; - default = true; + default = false; description = '' Operate in single user mode, disables all functionality related to @@ -445,17 +449,15 @@ let ###### implementation - config = let - root = "/var/lib/tt-rss"; - in mkIf cfg.enable { + config = mkIf cfg.enable { - services.phpfpm.poolConfigs = if cfg.pool == "${poolName}" then { + services.phpfpm.poolConfigs = mkIf (cfg.pool == "${poolName}") { "${poolName}" = '' listen = "${phpfpmSocketName}"; listen.owner = nginx listen.group = nginx listen.mode = 0600 - user = nginx + user = ${cfg.user} pm = dynamic pm.max_children = 75 pm.start_servers = 10 @@ -464,36 +466,26 @@ let pm.max_requests = 500 catch_workers_output = 1 ''; - } else {}; - - # TODO: Re-enable after https://github.com/NixOS/nixpkgs/pull/15862 is merged - - # services.nginx.virtualHosts = if cfg.virtualHost == "${virtualHostName}" then { - # "${virtualHostName}" = { - # root = "${root}"; - # extraConfig = '' - # access_log /var/log/nginx-${virtualHostName}-access.log; - # error_log /var/log/nginx-${virtualHostName}-error.log; - # ''; - - # locations."/" = { - # extraConfig = '' - # index index.php; - # ''; - # }; - - # locations."~ \.php$" = { - # extraConfig = '' - # fastcgi_split_path_info ^(.+\.php)(/.+)$; - # fastcgi_pass unix:${phpfpmSocketName}; - # fastcgi_index index.php; - # fastcgi_param SCRIPT_FILENAME ${root}/$fastcgi_script_name; - - # include ${pkgs.nginx}/conf/fastcgi_params; - # ''; - # }; - # }; - # } else {}; + }; + + services.nginx.virtualHosts = mkIf (cfg.virtualHost != null) { + "${cfg.virtualHost}" = { + root = "${cfg.root}"; + + locations."/" = { + index = "index.php"; + }; + + locations."~ \.php$" = { + extraConfig = '' + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass unix:${phpfpmSocketName}; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME ${cfg.root}/$fastcgi_script_name; + ''; + }; + }; + }; systemd.services.tt-rss = let @@ -503,35 +495,34 @@ let description = "Tiny Tiny RSS feeds update daemon"; preStart = let - callSql = if cfg.database.type == "pgsql" then (e: '' - ${optionalString (cfg.database.password != null) - "PGPASSWORD=${cfg.database.password}"} ${pkgs.postgresql95}/bin/psql \ - -U ${cfg.database.user} \ - -h ${cfg.database.host} \ - --port ${toString dbPort} \ - -c '${e}' \ - ${cfg.database.name}'') - - else if cfg.database.type == "mysql" then (e: '' - echo '${e}' | ${pkgs.mysql}/bin/mysql \ - ${optionalString (cfg.database.password != null) - "-p${cfg.database.password}"} \ - -u ${cfg.database.user} \ - -h ${cfg.database.host} \ - -P ${toString dbPort} \ - ${cfg.database.name}'') - - else ""; + callSql = e: + if cfg.database.type == "pgsql" then '' + ${optionalString (cfg.database.password != null) "PGPASSWORD=${cfg.database.password}"} \ + ${pkgs.postgresql95}/bin/psql \ + -U ${cfg.database.user} \ + ${optionalString (cfg.database.host != null) "-h ${cfg.database.host} --port ${toString dbPort}"} \ + -c '${e}' \ + ${cfg.database.name}'' + + else if cfg.database.type == "mysql" then '' + echo '${e}' | ${pkgs.mysql}/bin/mysql \ + -u ${cfg.database.user} \ + ${optionalString (cfg.database.password != null) "-p${cfg.database.password}"} \ + ${optionalString (cfg.database.host != null) "-h ${cfg.database.host} -P ${toString dbPort}"} \ + ${cfg.database.name}'' + + else ""; in '' - rm -rf "${root}/*" - mkdir -m 755 -p "${root}" - cp -r "${pkgs.tt-rss}/"* "${root}" - ln -sf "${tt-rss-config}" "${root}/config.php" - chown -R "${cfg.user}" "${root}" - chmod -R 755 "${root}" - '' + (optionalString (cfg.database.type == "pgsql") '' - + rm -rf "${cfg.root}/*" + mkdir -m 755 -p "${cfg.root}" + cp -r "${pkgs.tt-rss}/"* "${cfg.root}" + ln -sf "${tt-rss-config}" "${cfg.root}/config.php" + chown -R "${cfg.user}" "${cfg.root}" + chmod -R 755 "${cfg.root}" + '' + + + (optionalString (cfg.database.type == "pgsql") '' exists=$(${callSql "select count(*) > 0 from pg_tables where tableowner = user"} \ | tail -n+3 | head -n-2 | sed -e 's/[ \n\t]*//') @@ -540,8 +531,9 @@ let else echo 'The database contains some data. Leaving it as it is.' fi; - '') + (optionalString (cfg.database.type == "mysql") '' + '') + + (optionalString (cfg.database.type == "mysql") '' exists=$(${callSql "select count(*) > 0 from information_schema.tables where table_schema = schema()"} \ | tail -n+2 | sed -e 's/[ \n\t]*//') @@ -554,7 +546,7 @@ let serviceConfig = { User = "${cfg.user}"; - ExecStart = "${pkgs.php}/bin/php /var/lib/tt-rss/update.php --daemon"; + ExecStart = "${pkgs.php}/bin/php ${cfg.root}/update.php --daemon"; StandardOutput = "syslog"; StandardError = "syslog"; PermissionsStartOnly = true; diff --git a/nixos/modules/services/web-servers/apache-httpd/default.nix b/nixos/modules/services/web-servers/apache-httpd/default.nix index 397857ea085..dc0ca501a48 100644 --- a/nixos/modules/services/web-servers/apache-httpd/default.nix +++ b/nixos/modules/services/web-servers/apache-httpd/default.nix @@ -16,7 +16,19 @@ let phpMajorVersion = head (splitString "." php.version); - getPort = cfg: if cfg.port != 0 then cfg.port else if cfg.enableSSL then 443 else 80; + mod_perl = pkgs.mod_perl.override { apacheHttpd = httpd; }; + + defaultListen = cfg: if cfg.enableSSL + then [{ip = "*"; port = 443;}] + else [{ip = "*"; port = 80;}]; + + getListen = cfg: + let list = (lib.optional (cfg.port != 0) {ip = "*"; port = cfg.port;}) ++ cfg.listen; + in if list == [] + then defaultListen cfg + else list; + + listenToString = l: "${l.ip}:${toString l.port}"; extraModules = attrByPath ["extraModules"] [] mainCfg; extraForeignModules = filter isAttrs extraModules; @@ -25,10 +37,13 @@ let makeServerInfo = cfg: { # Canonical name must not include a trailing slash. - canonicalName = - (if cfg.enableSSL then "https" else "http") + "://" + - cfg.hostName + - (if getPort cfg != (if cfg.enableSSL then 443 else 80) then ":${toString (getPort cfg)}" else ""); + canonicalNames = + let defaultPort = (head (defaultListen cfg)).port; in + map (port: + (if cfg.enableSSL then "https" else "http") + "://" + + cfg.hostName + + (if port != defaultPort then ":${toString port}" else "") + ) (map (x: x.port) (getListen cfg)); # Admin address: inherit from the main server if not specified for # a virtual host. @@ -63,6 +78,7 @@ let robotsEntries = ""; startupScript = ""; enablePHP = false; + enablePerl = false; phpOptions = ""; options = {}; documentRoot = null; @@ -224,7 +240,7 @@ let ++ (map (svc: svc.robotsEntries) subservices))); in '' - ServerName ${serverInfo.canonicalName} + ${concatStringsSep "\n" (map (n: "ServerName ${n}") serverInfo.canonicalNames)} ${concatMapStrings (alias: "ServerAlias ${alias}\n") cfg.serverAliases} @@ -326,9 +342,10 @@ let </IfModule> ${let - ports = map getPort allHosts; - uniquePorts = uniqList {inputList = ports;}; - in concatMapStrings (port: "Listen ${toString port}\n") uniquePorts + listen = concatMap getListen allHosts; + toStr = listen: "Listen ${listenToString listen}\n"; + uniqueListen = uniqList {inputList = map toStr listen;}; + in concatStrings uniqueListen } User ${mainCfg.user} @@ -341,6 +358,7 @@ let ++ map (name: {inherit name; path = "${httpd}/modules/mod_${name}.so";}) apacheModules ++ optional mainCfg.enableMellon { name = "auth_mellon"; path = "${pkgs.apacheHttpdPackages.mod_auth_mellon}/modules/mod_auth_mellon.so"; } ++ optional enablePHP { name = "php${phpMajorVersion}"; path = "${php}/modules/libphp${phpMajorVersion}.so"; } + ++ optional enablePerl { name = "perl"; path = "${mod_perl}/modules/mod_perl.so"; } ++ concatMap (svc: svc.extraModules) allSubservices ++ extraForeignModules; in concatMapStrings load allModules @@ -382,15 +400,15 @@ let # Always enable virtual hosts; it doesn't seem to hurt. ${let - ports = map getPort allHosts; - uniquePorts = uniqList {inputList = ports;}; - directives = concatMapStrings (port: "NameVirtualHost *:${toString port}\n") uniquePorts; + listen = concatMap getListen allHosts; + uniqueListen = uniqList {inputList = listen;}; + directives = concatMapStrings (listen: "NameVirtualHost ${listenToString listen}\n") uniqueListen; in optionalString (!version24) directives } ${let makeVirtualHost = vhost: '' - <VirtualHost *:${toString (getPort vhost)}> + <VirtualHost ${concatStringsSep " " (map listenToString (getListen vhost))}> ${perServerConf false vhost} </VirtualHost> ''; @@ -401,6 +419,8 @@ let enablePHP = mainCfg.enablePHP || any (svc: svc.enablePHP) allSubservices; + enablePerl = mainCfg.enablePerl || any (svc: svc.enablePerl) allSubservices; + # Generate the PHP configuration file. Should probably be factored # out into a separate module. @@ -565,6 +585,12 @@ in ''; }; + enablePerl = mkOption { + type = types.bool; + default = false; + description = "Whether to enable the Perl module (mod_perl)."; + }; + phpOptions = mkOption { type = types.lines; default = ""; @@ -628,6 +654,8 @@ in message = "SSL is enabled for httpd, but sslServerCert and/or sslServerKey haven't been specified."; } ]; + warnings = map (cfg: ''apache-httpd's port option is deprecated. Use listen = [{/*ip = "*"; */ port = ${toString cfg.port}";}]; instead'' ) (lib.filter (cfg: cfg.port != 0) allHosts); + users.extraUsers = optionalAttrs (mainCfg.user == "wwwrun") (singleton { name = "wwwrun"; group = mainCfg.group; @@ -681,13 +709,6 @@ in ''} mkdir -m 0700 -p ${mainCfg.logDir} - ${optionalString (mainCfg.documentRoot != null) - '' - # Create the document root directory if does not exists yet - mkdir -p ${mainCfg.documentRoot} - '' - } - # Get rid of old semaphores. These tend to accumulate across # server restarts, eventually preventing it from restarting # successfully. @@ -712,5 +733,4 @@ in }; }; - } diff --git a/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix b/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix index b4b5a6fdc07..1ed489bcb09 100644 --- a/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix +++ b/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix @@ -83,11 +83,11 @@ let # Unpack Mediawiki and put the config file in its root directory. mediawikiRoot = pkgs.stdenv.mkDerivation rec { - name= "mediawiki-1.23.13"; + name= "mediawiki-1.27.1"; src = pkgs.fetchurl { - url = "http://download.wikimedia.org/mediawiki/1.23/${name}.tar.gz"; - sha256 = "168wpf53n4ksj2g5q5r0hxapx6238dvsfng5ff9ixk6axsn0j5d0"; + url = "http://download.wikimedia.org/mediawiki/1.27/${name}.tar.gz"; + sha256 = "0sm3ymz93qragbwhzzbwq7f127mbj29inv0afg2z6p32jb1pd9h8"; }; skins = config.skins; @@ -288,6 +288,7 @@ in }; extraConfig = mkOption { + type = types.lines; default = ""; example = '' diff --git a/nixos/modules/services/web-servers/apache-httpd/moodle.nix b/nixos/modules/services/web-servers/apache-httpd/moodle.nix index 87b1fba5aa1..d525348d5c7 100644 --- a/nixos/modules/services/web-servers/apache-httpd/moodle.nix +++ b/nixos/modules/services/web-servers/apache-httpd/moodle.nix @@ -63,6 +63,10 @@ let cp -r * $out cp ${moodleConfig} $out/config.php ''; + # Marked as broken due to needing an update for security issues. + # See: https://github.com/NixOS/nixpkgs/issues/18856 + meta.broken = true; + }; in @@ -160,6 +164,7 @@ in extraConfig = mkOption { + type = types.lines; default = ""; example = '' diff --git a/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix b/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix index 5abcc5e7490..1d53ce65900 100644 --- a/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix +++ b/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix @@ -28,9 +28,30 @@ with lib; type = types.int; default = 0; description = '' - Port for the server. 0 means use the default port: 80 for http - and 443 for https (i.e. when enableSSL is set). + Port for the server. Option will be removed, use <option>listen</option> instead. + ''; + }; + + listen = mkOption { + type = types.listOf (types.submodule ( + { + options = { + port = mkOption { + type = types.int; + description = "port to listen on"; + }; + ip = mkOption { + type = types.string; + default = "*"; + description = "Ip to listen on. 0.0.0.0 for ipv4 only, * for all."; + }; + }; + } )); + description = '' + List of { /* ip: "*"; */ port = 80;} to listen on ''; + + default = []; }; enableSSL = mkOption { diff --git a/nixos/modules/services/web-servers/apache-httpd/trac.nix b/nixos/modules/services/web-servers/apache-httpd/trac.nix index 3196edc2838..35b9ab56087 100644 --- a/nixos/modules/services/web-servers/apache-httpd/trac.nix +++ b/nixos/modules/services/web-servers/apache-httpd/trac.nix @@ -99,10 +99,9 @@ in makeSearchPathOutput "lib" "lib/${pkgs.python.libPrefix}/site-packages" [ pkgs.mod_python pkgs.pythonPackages.trac - pkgs.setuptools + pkgs.pythonPackages.setuptools pkgs.pythonPackages.genshi pkgs.pythonPackages.psycopg2 - pkgs.python.modules.sqlite3 subversion ]; }; diff --git a/nixos/modules/services/web-servers/apache-httpd/wordpress.nix b/nixos/modules/services/web-servers/apache-httpd/wordpress.nix index 937b2698ce9..32dd4439675 100644 --- a/nixos/modules/services/web-servers/apache-httpd/wordpress.nix +++ b/nixos/modules/services/web-servers/apache-httpd/wordpress.nix @@ -5,7 +5,8 @@ with lib; let - version = "4.3.1"; + # Upgrading? We have a test! nix-build ./nixos/tests/wordpress.nix + version = "4.6.1"; fullversion = "${version}"; # Our bare-bones wp-config.php file using the above settings @@ -74,7 +75,7 @@ let owner = "WordPress"; repo = "WordPress"; rev = "${fullversion}"; - sha256 = "1rk10vcv4z9p04hfzc0wkbilrgx7m9ssyr6c3w6vw3vl1bcgqxza"; + sha256 = "0n82xgjg1ry2p73hhgpslnkdzrma5n6hxxq76s7qskkzj0qjfvpn"; }; installPhase = '' mkdir -p $out @@ -98,7 +99,7 @@ let # symlink additional plugin(s) ${concatMapStrings (plugin: "ln -s ${plugin} $out/wp-content/plugins/${plugin.name}\n") (config.plugins) } - # symlink additional translation(s) + # symlink additional translation(s) mkdir -p $out/wp-content/languages ${concatMapStrings (language: "ln -s ${language}/*.mo ${language}/*.po $out/wp-content/languages/\n") (selectedLanguages) } ''; @@ -123,7 +124,7 @@ in options = { dbHost = mkOption { default = "localhost"; - description = "The location of the database server."; + description = "The location of the database server."; example = "localhost"; }; dbName = mkOption { @@ -211,6 +212,7 @@ in example = "[ \"en_GB\" \"de_DE\" ];"; }; extraConfig = mkOption { + type = types.lines; default = ""; example = '' @@ -244,7 +246,6 @@ in chown ${serverInfo.serverConfig.user} ${config.wordpressUploads} # we should use systemd dependencies here - #waitForUnit("network-interfaces.target"); if [ ! -d ${serverInfo.fullConfig.services.mysql.dataDir}/${config.dbName} ]; then echo "Need to create the database '${config.dbName}' and grant permissions to user named '${config.dbUser}'." # Wait until MySQL is up @@ -253,7 +254,7 @@ in done ${pkgs.mysql}/bin/mysql -e 'CREATE DATABASE ${config.dbName};' ${pkgs.mysql}/bin/mysql -e 'GRANT ALL ON ${config.dbName}.* TO ${config.dbUser}@localhost IDENTIFIED BY "${config.dbPassword}";' - else + else echo "Good, no need to do anything database related." fi ''; diff --git a/nixos/modules/services/web-servers/fcgiwrap.nix b/nixos/modules/services/web-servers/fcgiwrap.nix index 2c5e433003c..a64a187255a 100644 --- a/nixos/modules/services/web-servers/fcgiwrap.nix +++ b/nixos/modules/services/web-servers/fcgiwrap.nix @@ -21,7 +21,7 @@ in { }; socketType = mkOption { - type = types.addCheck types.str (t: t == "unix" || t == "tcp" || t == "tcp6"); + type = types.enum [ "unix" "tcp" "tcp6" ]; default = "unix"; description = "Socket type: 'unix', 'tcp' or 'tcp6'."; }; diff --git a/nixos/modules/services/web-servers/lighttpd/inginious.nix b/nixos/modules/services/web-servers/lighttpd/inginious.nix index 43deccb6aef..669e81d0f14 100644 --- a/nixos/modules/services/web-servers/lighttpd/inginious.nix +++ b/nixos/modules/services/web-servers/lighttpd/inginious.nix @@ -191,9 +191,8 @@ in virtualisation.docker = { enable = true; # We need docker to listen on port 2375. - extraOptions = "-H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock"; + listenOptions = ["127.0.0.1:2375" "/var/run/docker.sock"]; storageDriver = mkDefault "overlay"; - socketActivation = false; }; users.extraUsers."lighttpd".extraGroups = [ "docker" ]; diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix index 6e62606f323..68a672c42c9 100644 --- a/nixos/modules/services/web-servers/nginx/default.nix +++ b/nixos/modules/services/web-servers/nginx/default.nix @@ -10,6 +10,7 @@ let sslCertificateKey = "/var/lib/acme/${vhostName}/key.pem"; }) ) cfg.virtualHosts; + enableIPv6 = config.networking.enableIPv6; configFile = pkgs.writeText "nginx.conf" '' user ${cfg.user} ${cfg.group}; @@ -18,9 +19,13 @@ let ${cfg.config} - ${optionalString (cfg.httpConfig == "" && cfg.config == "") '' - events {} + ${optionalString (cfg.eventsConfig != "" || cfg.config == "") '' + events { + ${cfg.eventsConfig} + } + ''} + ${optionalString (cfg.httpConfig == "" && cfg.config == "") '' http { include ${cfg.package}/conf/mime.types; include ${cfg.package}/conf/fastcgi.conf; @@ -80,7 +85,7 @@ let ${optionalString cfg.statusPage '' server { listen 80; - listen [::]:80; + ${optionalString enableIPv6 "listen [::]:80;" } server_name localhost; @@ -88,7 +93,7 @@ let stub_status on; access_log off; allow 127.0.0.1; - allow ::1; + ${optionalString enableIPv6 "allow ::1;"} deny all; } } @@ -98,7 +103,6 @@ let }''} ${optionalString (cfg.httpConfig != "") '' - events {} http { include ${cfg.package}/conf/mime.types; include ${cfg.package}/conf/fastcgi.conf; @@ -113,35 +117,38 @@ let ssl = vhost.enableSSL || vhost.forceSSL; port = if vhost.port != null then vhost.port else (if ssl then 443 else 80); listenString = toString port + optionalString ssl " ssl http2" - + optionalString vhost.default " default"; - acmeLocation = optionalString vhost.enableACME '' + + optionalString vhost.default " default_server"; + acmeLocation = optionalString vhost.enableACME ('' location /.well-known/acme-challenge { - try_files $uri @acme-fallback; + ${optionalString (vhost.acmeFallbackHost != null) "try_files $uri @acme-fallback;"} root ${vhost.acmeRoot}; auth_basic off; } + '' + (optionalString (vhost.acmeFallbackHost != null) '' location @acme-fallback { auth_basic off; proxy_pass http://${vhost.acmeFallbackHost}; } - ''; + '')); in '' ${optionalString vhost.forceSSL '' server { - listen 80 ${optionalString vhost.default "default"}; - listen [::]:80 ${optionalString vhost.default "default"}; + listen 80 ${optionalString vhost.default "default_server"}; + ${optionalString enableIPv6 + ''listen [::]:80 ${optionalString vhost.default "default_server"};'' + } server_name ${serverName} ${concatStringsSep " " vhost.serverAliases}; ${acmeLocation} location / { - return 301 https://$host${optionalString (port != 443) ":${port}"}$request_uri; + return 301 https://$host${optionalString (port != 443) ":${toString port}"}$request_uri; } } ''} server { listen ${listenString}; - listen [::]:${listenString}; + ${optionalString enableIPv6 "listen [::]:${listenString};"} server_name ${serverName} ${concatStringsSep " " vhost.serverAliases}; ${acmeLocation} @@ -271,12 +278,20 @@ in "; }; + eventsConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Configuration lines to be set inside the events block. + ''; + }; + appendHttpConfig = mkOption { type = types.lines; default = ""; description = " Configuration lines to be appended to the generated http block. - This is mutually exclusive with using config and httpConfig for + This is mutually exclusive with using config and httpConfig for specifying the whole http block verbatim. "; }; @@ -380,8 +395,13 @@ in security.acme.certs = filterAttrs (n: v: v != {}) ( mapAttrs (vhostName: vhostConfig: optionalAttrs vhostConfig.enableACME { + user = cfg.user; + group = cfg.group; webroot = vhostConfig.acmeRoot; extraDomains = genAttrs vhostConfig.serverAliases (alias: null); + postRun = '' + systemctl reload nginx + ''; } ) virtualHosts ); diff --git a/nixos/modules/services/web-servers/nginx/vhost-options.nix b/nixos/modules/services/web-servers/nginx/vhost-options.nix index ee3f68bf805..dcebbc9229f 100644 --- a/nixos/modules/services/web-servers/nginx/vhost-options.nix +++ b/nixos/modules/services/web-servers/nginx/vhost-options.nix @@ -39,8 +39,8 @@ with lib; }; acmeFallbackHost = mkOption { - type = types.str; - default = "0.0.0.0"; + type = types.nullOr types.str; + default = null; description = '' Host which to proxy requests to if acme challenge is not found. Useful if you want multiple hosts to be able to verify the same domain name. diff --git a/nixos/modules/services/web-servers/phpfpm/default.nix b/nixos/modules/services/web-servers/phpfpm/default.nix index 29cfbb8e9a0..ed537e7122a 100644 --- a/nixos/modules/services/web-servers/phpfpm/default.nix +++ b/nixos/modules/services/web-servers/phpfpm/default.nix @@ -7,8 +7,6 @@ let stateDir = "/run/phpfpm"; - pidFile = "${stateDir}/phpfpm.pid"; - mkPool = n: p: '' [${n}] listen = ${p.listen} @@ -17,9 +15,8 @@ let cfgFile = pkgs.writeText "phpfpm.conf" '' [global] - pid = ${pidFile} error_log = syslog - daemonize = yes + daemonize = no ${cfg.extraConfig} ${concatStringsSep "\n" (mapAttrsToList mkPool cfg.pools)} @@ -42,8 +39,8 @@ in { default = ""; description = '' Extra configuration that should be put in the global section of - the PHP FPM configuration file. Do not specify the options - <literal>pid</literal>, <literal>error_log</literal> or + the PHP-FPM configuration file. Do not specify the options + <literal>error_log</literal> or <literal>daemonize</literal> here, since they are generated by NixOS. ''; @@ -54,7 +51,7 @@ in { default = pkgs.php; defaultText = "pkgs.php"; description = '' - The PHP package to use for running the FPM service. + The PHP package to use for running the PHP-FPM service. ''; }; @@ -86,7 +83,7 @@ in { } ''; description = '' - A mapping between PHP FPM pool names and their configurations. + A mapping between PHP-FPM pool names and their configurations. See the documentation on <literal>php-fpm.conf</literal> for details on configuration directives. If no pools are defined, the phpfpm service is disabled. @@ -98,8 +95,24 @@ in { inherit lib; })); default = {}; + example = literalExample '' + { + mypool = { + listen = "/path/to/unix/socket"; + extraConfig = ''' + user = nobody + pm = dynamic + pm.max_children = 75 + pm.start_servers = 10 + pm.min_spare_servers = 5 + pm.max_spare_servers = 20 + pm.max_requests = 500 + '''; + } + }''; description = '' - If no pools are defined, the phpfpm service is disabled. + PHP-FPM pools. If no pools or poolConfigs are defined, the PHP-FPM + service is disabled. ''; }; }; @@ -113,10 +126,10 @@ in { mkdir -p "${stateDir}" ''; serviceConfig = { + Type = "notify"; ExecStart = "${cfg.phpPackage}/bin/php-fpm -y ${cfgFile} -c ${phpIni}"; - PIDFile = pidFile; + ExecReload = "${pkgs.coreutils}/bin/kill -USR2 $MAINPID"; }; }; - }; } diff --git a/nixos/modules/services/web-servers/tomcat.nix b/nixos/modules/services/web-servers/tomcat.nix index c3be20b41e2..943415e08c6 100644 --- a/nixos/modules/services/web-servers/tomcat.nix +++ b/nixos/modules/services/web-servers/tomcat.nix @@ -10,6 +10,10 @@ in { + meta = { + maintainers = with maintainers; [ danbst ]; + }; + ###### interface options = { @@ -23,9 +27,9 @@ in package = mkOption { type = types.package; - default = pkgs.tomcat7; - defaultText = "pkgs.tomcat7"; - example = lib.literalExample "pkgs.tomcat8"; + default = pkgs.tomcat85; + defaultText = "pkgs.tomcat85"; + example = lib.literalExample "pkgs.tomcatUnstable"; description = '' Which tomcat package to use. ''; @@ -74,8 +78,8 @@ in webapps = mkOption { type = types.listOf types.package; - default = [ tomcat ]; - defaultText = "[ tomcat ]"; + default = [ tomcat.webapps ]; + defaultText = "[ tomcat.webapps ]"; description = "List containing WAR files or directories with WAR files which are web applications to be deployed on Tomcat"; }; @@ -135,7 +139,7 @@ in systemd.services.tomcat = { description = "Apache Tomcat server"; wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" ]; + after = [ "network.target" ]; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; @@ -352,7 +356,7 @@ in ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c 'CATALINA_BASE=${cfg.baseDir} JAVA_HOME=${cfg.jdk} JAVA_OPTS="${cfg.javaOpts}" CATALINA_OPTS="${cfg.catalinaOpts}" ${tomcat}/bin/startup.sh' ''; - postStop = '' + preStop = '' echo "Stopping tomcat..." CATALINA_BASE=${cfg.baseDir} JAVA_HOME=${cfg.jdk} ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c ${tomcat}/bin/shutdown.sh ''; diff --git a/nixos/modules/services/web-servers/uwsgi.nix b/nixos/modules/services/web-servers/uwsgi.nix index 56f077e62a8..14596bb3add 100644 --- a/nixos/modules/services/web-servers/uwsgi.nix +++ b/nixos/modules/services/web-servers/uwsgi.nix @@ -29,7 +29,6 @@ let pythonPackages = pkgs.pythonPackages.override { inherit python; - self = pythonPackages; }; penv = python.buildEnv.override { diff --git a/nixos/modules/services/web-servers/winstone.nix b/nixos/modules/services/web-servers/winstone.nix index 6dab467b35e..064ead5ce4b 100644 --- a/nixos/modules/services/web-servers/winstone.nix +++ b/nixos/modules/services/web-servers/winstone.nix @@ -113,8 +113,7 @@ in { options = { services.winstone = mkOption { default = {}; - type = types.attrsOf types.optionSet; - options = [ winstoneOpts ]; + type = with types; attrsOf (submodule winstoneOpts); description = '' Defines independent Winstone services, each serving one WAR-file. ''; diff --git a/nixos/modules/services/web-servers/zope2.nix b/nixos/modules/services/web-servers/zope2.nix index ef3cffd582e..8a453e01557 100644 --- a/nixos/modules/services/web-servers/zope2.nix +++ b/nixos/modules/services/web-servers/zope2.nix @@ -74,7 +74,7 @@ in services.zope2.instances = mkOption { default = {}; - type = types.loaOf types.optionSet; + type = with types; loaOf (submodule zope2Opts); example = literalExample '' { plone01 = { @@ -96,7 +96,6 @@ in } ''; description = "zope2 instances to be created automaticaly by the system."; - options = [ zope2Opts ]; }; }; diff --git a/nixos/modules/services/x11/compton.nix b/nixos/modules/services/x11/compton.nix index bda4eec0102..7cbca1dcddf 100644 --- a/nixos/modules/services/x11/compton.nix +++ b/nixos/modules/services/x11/compton.nix @@ -188,6 +188,7 @@ in { package = mkOption { type = types.package; default = pkgs.compton; + defaultText = "pkgs.compton"; example = literalExample "pkgs.compton"; description = '' Compton derivation to use. diff --git a/nixos/modules/services/x11/desktop-managers/default.nix b/nixos/modules/services/x11/desktop-managers/default.nix index 1ea7b5ccf16..144e4aada27 100644 --- a/nixos/modules/services/x11/desktop-managers/default.nix +++ b/nixos/modules/services/x11/desktop-managers/default.nix @@ -19,7 +19,8 @@ in # E.g., if KDE is enabled, it supersedes xterm. imports = [ ./none.nix ./xterm.nix ./xfce.nix ./kde4.nix ./kde5.nix - ./enlightenment.nix ./gnome3.nix ./kodi.nix + ./lumina.nix ./lxqt.nix ./enlightenment.nix ./gnome3.nix + ./kodi.nix ]; options = { diff --git a/nixos/modules/services/x11/desktop-managers/enlightenment.nix b/nixos/modules/services/x11/desktop-managers/enlightenment.nix index 90803ede9d9..9d0ff77c2ae 100644 --- a/nixos/modules/services/x11/desktop-managers/enlightenment.nix +++ b/nixos/modules/services/x11/desktop-managers/enlightenment.nix @@ -32,10 +32,10 @@ in e.efl e.enlightenment e.terminology e.econnman pkgs.xorg.xauth # used by kdesu - pkgs.gtk # To get GTK+'s themes. + pkgs.gtk2 # To get GTK+'s themes. pkgs.tango-icon-theme pkgs.shared_mime_info - pkgs.gnome.gnomeicontheme + pkgs.gnome2.gnomeicontheme pkgs.xorg.xcursorthemes ]; diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix index b3da2544802..17e84b1d9a1 100644 --- a/nixos/modules/services/x11/desktop-managers/gnome3.nix +++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix @@ -25,9 +25,8 @@ let ''; }; - nixos-gsettings-desktop-schemas = pkgs.stdenv.mkDerivation { - name = "nixos-gsettings-desktop-schemas"; - buildCommand = '' + nixos-gsettings-desktop-schemas = pkgs.runCommand "nixos-gsettings-desktop-schemas" {} + '' mkdir -p $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas cp -rf ${gnome3.gsettings_desktop_schemas}/share/gsettings-schemas/gsettings-desktop-schemas*/glib-2.0/schemas/*.xml $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas @@ -46,7 +45,6 @@ let ${pkgs.glib.dev}/bin/glib-compile-schemas $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas/ ''; - }; in { @@ -84,7 +82,7 @@ in { environment.gnome3.packageSet = mkOption { default = null; - example = literalExample "pkgs.gnome3_20"; + example = literalExample "pkgs.gnome3_22"; description = "Which GNOME 3 package set to use."; apply = p: if p == null then pkgs.gnome3 else p; }; @@ -110,6 +108,7 @@ in { services.gnome3.gnome-documents.enable = mkDefault true; services.gnome3.gnome-keyring.enable = true; services.gnome3.gnome-online-accounts.enable = mkDefault true; + services.gnome3.gnome-terminal-server.enable = mkDefault true; services.gnome3.gnome-user-share.enable = mkDefault true; services.gnome3.gvfs.enable = true; services.gnome3.seahorse.enable = mkDefault true; @@ -124,6 +123,7 @@ in { services.packagekit.enable = mkDefault true; hardware.bluetooth.enable = mkDefault true; services.xserver.libinput.enable = mkDefault true; # for controlling touchpad settings via gnome control center + services.udev.packages = [ pkgs.gnome3.gnome_settings_daemon ]; fonts.fonts = [ pkgs.dejavu_fonts pkgs.cantarell_fonts ]; diff --git a/nixos/modules/services/x11/desktop-managers/kde4.nix b/nixos/modules/services/x11/desktop-managers/kde4.nix index 88b3c3a1016..31d2ebcdf1a 100644 --- a/nixos/modules/services/x11/desktop-managers/kde4.nix +++ b/nixos/modules/services/x11/desktop-managers/kde4.nix @@ -14,7 +14,7 @@ let # files), segfault sometimes and consume significant resources. # They can be re-enabled in the KDE System Settings under "Desktop # Search". - nepomukConfig = pkgs.writeTextFile + disableNepomuk = pkgs.writeTextFile { name = "nepomuk-config"; destination = "/share/config/nepomukserverrc"; text = @@ -70,6 +70,18 @@ in type = types.package; description = "Custom kde-workspace, used for NixOS rebranding."; }; + + enablePIM = mkOption { + type = types.bool; + default = true; + description = "Whether to enable PIM support. Note that enabling this pulls in Akonadi and MariaDB as dependencies."; + }; + + enableNepomuk = mkOption { + type = types.bool; + default = false; + description = "Whether to enable Nepomuk (deprecated)."; + }; }; }; @@ -138,7 +150,6 @@ in pkgs.kde4.kde_wallpapers # contains kdm's default background pkgs.kde4.oxygen_icons - pkgs.virtuoso # to enable Nepomuk to find Virtuoso # Starts KDE's Polkit authentication agent. pkgs.kde4.polkit_kde_agent @@ -149,20 +160,26 @@ in xorg.xmessage # so that startkde can show error messages xorg.xset # used by startkde, non-essential xorg.xauth # used by kdesu - pkgs.shared_desktop_ontologies # used by nepomuk - pkgs.strigi # used by nepomuk + ] + ++ optionals cfg.enablePIM + [ pkgs.kde4.kdepim_runtime pkgs.kde4.akonadi pkgs.mysql # used by akonadi - pkgs.kde4.kdepim_runtime ] - ++ lib.optional config.hardware.pulseaudio.enable pkgs.kde4.kmix # Perhaps this should always be enabled - ++ lib.optional config.hardware.bluetooth.enable pkgs.kde4.bluedevil - ++ lib.optional config.networking.networkmanager.enable pkgs.kde4.plasma-nm - ++ [ nepomukConfig ] ++ phononBackendPackages; + ++ (if cfg.enableNepomuk then + [ pkgs.shared_desktop_ontologies # used by nepomuk + pkgs.strigi # used by nepomuk + pkgs.virtuoso # to enable Nepomuk to find Virtuoso + ] else + [ disableNepomuk ]) + ++ optional config.hardware.pulseaudio.enable pkgs.kde4.kmix # Perhaps this should always be enabled + ++ optional config.hardware.bluetooth.enable pkgs.kde4.bluedevil + ++ optional config.networking.networkmanager.enable pkgs.kde4.plasma-nm + ++ phononBackendPackages; environment.pathsToLink = [ "/share" ]; - environment.profileRelativeEnvVars = mkIf (lib.elem "gstreamer" cfg.phononBackends) { + environment.profileRelativeEnvVars = mkIf (elem "gstreamer" cfg.phononBackends) { GST_PLUGIN_SYSTEM_PATH = [ "/lib/gstreamer-0.10" ]; }; diff --git a/nixos/modules/services/x11/desktop-managers/kde5.nix b/nixos/modules/services/x11/desktop-managers/kde5.nix index 7856ff03f16..3037f949cbf 100644 --- a/nixos/modules/services/x11/desktop-managers/kde5.nix +++ b/nixos/modules/services/x11/desktop-managers/kde5.nix @@ -22,207 +22,231 @@ in description = "Enable the Plasma 5 (KDE 5) desktop environment."; }; + enableQt4Support = mkOption { + type = types.bool; + default = true; + description = '' + Enable support for Qt 4-based applications. Particularly, install the + Qt 4 version of the Breeze theme and a default backend for Phonon. + ''; + }; + + extraPackages = mkOption { + type = types.listOf types.package; + default = []; + description = '' + KDE packages that need to be installed system-wide. + ''; + }; + }; }; - config = mkIf (xcfg.enable && cfg.enable) { + config = mkMerge [ + (mkIf (cfg.extraPackages != []) { + environment.systemPackages = [ (kde5.kdeWrapper cfg.extraPackages) ]; + }) - warnings = optional config.services.xserver.desktopManager.kde4.enable - "KDE 4 should not be enabled at the same time as KDE 5"; + (mkIf (xcfg.enable && cfg.enable) { - services.xserver.desktopManager.session = singleton { - name = "kde5"; - bgSupport = true; - start = '' - # Load PulseAudio module for routing support. - # See http://colin.guthr.ie/2009/10/so-how-does-the-kde-pulseaudio-support-work-anyway/ - ${optionalString config.hardware.pulseaudio.enable '' - ${getBin config.hardware.pulseaudio.package}/bin/pactl load-module module-device-manager "do_routing=1" - ''} + warnings = optional config.services.xserver.desktopManager.kde4.enable + "KDE 4 should not be enabled at the same time as KDE 5"; - exec "${kde5.startkde}" + services.xserver.desktopManager.session = singleton { + name = "kde5"; + bgSupport = true; + start = '' + # Load PulseAudio module for routing support. + # See http://colin.guthr.ie/2009/10/so-how-does-the-kde-pulseaudio-support-work-anyway/ + ${optionalString config.hardware.pulseaudio.enable '' + ${getBin config.hardware.pulseaudio.package}/bin/pactl load-module module-device-manager "do_routing=1" + ''} - ''; - }; + exec "${kde5.startkde}" + ''; + }; - security.permissionsWrappers.setuid = [ - { - program = "kcheckpass"; - source = "${kde5.plasma-workspace.out}/lib/libexec/kcheckpass"; - owner = "root"; - setuid = true; - } - { - program = "start_kdeinit"; - source = "${kde5.kinit.out}/lib/libexec/kf5/start_kdeinit"; - owner = "root"; - setuid = true; - } - ]; - - environment.systemPackages = - [ - kde5.frameworkintegration - kde5.kactivities - kde5.kauth - kde5.kcmutils - kde5.kconfig - kde5.kconfigwidgets - kde5.kcoreaddons - kde5.kdbusaddons - kde5.kdeclarative - kde5.kded - kde5.kdesu - kde5.kdnssd - kde5.kemoticons - kde5.kfilemetadata - kde5.kglobalaccel - kde5.kguiaddons - kde5.kiconthemes - kde5.kidletime - kde5.kimageformats - kde5.kinit - kde5.kio - kde5.kjobwidgets - kde5.knewstuff - kde5.knotifications - kde5.knotifyconfig - kde5.kpackage - kde5.kparts - kde5.kpeople - kde5.krunner - kde5.kservice - kde5.ktextwidgets - kde5.kwallet - kde5.kwayland - kde5.kwidgetsaddons - kde5.kxmlgui - kde5.kxmlrpcclient - kde5.plasma-framework - kde5.solid - kde5.sonnet - kde5.threadweaver - - kde5.breeze - kde5.kactivitymanagerd - kde5.kde-cli-tools - kde5.kdecoration - kde5.kdeplasma-addons - kde5.kgamma5 - kde5.khelpcenter - kde5.khotkeys - kde5.kinfocenter - kde5.kmenuedit - kde5.kscreen - kde5.kscreenlocker - kde5.ksysguard - kde5.kwayland - kde5.kwin - kde5.kwrited - kde5.libkscreen - kde5.libksysguard - kde5.milou - kde5.oxygen - kde5.plasma-integration - kde5.polkit-kde-agent - kde5.systemsettings - - kde5.plasma-desktop - kde5.plasma-workspace - kde5.plasma-workspace-wallpapers - - kde5.dolphin - kde5.dolphin-plugins - kde5.ffmpegthumbs - kde5.kdegraphics-thumbnailers - kde5.kio-extras - kde5.konsole - kde5.print-manager - - # Oxygen icons moved to KDE Frameworks 5.16 and later. - (kde5.oxygen-icons or kde5.oxygen-icons5) - pkgs.hicolor_icon_theme - - kde5.kde-gtk-config - - pkgs.phonon-backend-gstreamer - pkgs.qt5.phonon-backend-gstreamer - ] - - # Plasma 5.5 and later has a Breeze GTK theme. - # If it is not available, Orion is very similar to Breeze. - ++ lib.optional (!(lib.hasAttr "breeze-gtk" kde5)) pkgs.orion - - # Install Breeze icons if available - ++ lib.optional (lib.hasAttr "breeze-icons" kde5) kde5.breeze-icons - - # Install activity manager if available - ++ lib.optional (lib.hasAttr "kactivitymanagerd" kde5) kde5.kactivitymanagerd - - # frameworkintegration was split with plasma-integration in Plasma 5.6 - ++ lib.optional (lib.hasAttr "plasma-integration" kde5) kde5.plasma-integration - - # Optional hardware support features - ++ lib.optional config.hardware.bluetooth.enable kde5.bluedevil - ++ lib.optional config.networking.networkmanager.enable kde5.plasma-nm - ++ lib.optional config.hardware.pulseaudio.enable kde5.plasma-pa - ++ lib.optional config.powerManagement.enable kde5.powerdevil - ++ lib.optional config.services.colord.enable pkgs.colord-kde - ++ lib.optionals config.services.samba.enable [ kde5.kdenetwork-filesharing pkgs.samba ]; - - environment.pathsToLink = [ "/share" ]; - - environment.etc = singleton { - source = "${pkgs.xkeyboard_config}/etc/X11/xkb"; - target = "X11/xkb"; - }; + security.permissionsWrappers.setuid = [ + { + program = "kcheckpass"; + source = "${kde5.plasma-workspace.out}/lib/libexec/kcheckpass"; + owner = "root"; + setuid = true; + } + { + program = "start_kdeinit"; + source = "${kde5.kinit.out}/lib/libexec/kf5/start_kdeinit"; + owner = "root"; + setuid = true; + } + ]; + + environment.systemPackages = + [ + kde5.frameworkintegration + kde5.kactivities + kde5.kauth + kde5.kcmutils + kde5.kconfig + kde5.kconfigwidgets + kde5.kcoreaddons + kde5.kdbusaddons + kde5.kdeclarative + kde5.kded + kde5.kdesu + kde5.kdnssd + kde5.kemoticons + kde5.kfilemetadata + kde5.kglobalaccel + kde5.kguiaddons + kde5.kiconthemes + kde5.kidletime + kde5.kimageformats + kde5.kinit + kde5.kio + kde5.kjobwidgets + kde5.knewstuff + kde5.knotifications + kde5.knotifyconfig + kde5.kpackage + kde5.kparts + kde5.kpeople + kde5.krunner + kde5.kservice + kde5.ktextwidgets + kde5.kwallet + kde5.kwayland + kde5.kwidgetsaddons + kde5.kxmlgui + kde5.kxmlrpcclient + kde5.plasma-framework + kde5.solid + kde5.sonnet + kde5.threadweaver + + kde5.breeze-qt5 + kde5.kactivitymanagerd + kde5.kde-cli-tools + kde5.kdecoration + kde5.kdeplasma-addons + kde5.kgamma5 + kde5.khotkeys + kde5.kinfocenter + kde5.kmenuedit + kde5.kscreen + kde5.kscreenlocker + kde5.ksysguard + kde5.kwayland + kde5.kwin + kde5.kwrited + kde5.libkscreen + kde5.libksysguard + kde5.milou + kde5.plasma-integration + kde5.polkit-kde-agent + kde5.systemsettings + + kde5.plasma-desktop + kde5.plasma-workspace + kde5.plasma-workspace-wallpapers + + kde5.dolphin-plugins + kde5.ffmpegthumbs + kde5.kdegraphics-thumbnailers + kde5.kio-extras + kde5.print-manager + + # Install Breeze icons if available + (kde5.breeze-icons or kde5.oxygen-icons5 or kde5.oxygen-icons) + pkgs.hicolor_icon_theme + + kde5.kde-gtk-config kde5.breeze-gtk + + pkgs.qt5.phonon-backend-gstreamer + ] + + # Plasma 5.5 and later has a Breeze GTK theme. + # If it is not available, Orion is very similar to Breeze. + ++ lib.optional (!(lib.hasAttr "breeze-gtk" kde5)) pkgs.orion + + # Install activity manager if available + ++ lib.optional (lib.hasAttr "kactivitymanagerd" kde5) kde5.kactivitymanagerd + + # frameworkintegration was split with plasma-integration in Plasma 5.6 + ++ lib.optional (lib.hasAttr "plasma-integration" kde5) kde5.plasma-integration + + ++ lib.optionals cfg.enableQt4Support [ kde5.breeze-qt4 pkgs.phonon-backend-gstreamer ] + + # Optional hardware support features + ++ lib.optional config.hardware.bluetooth.enable kde5.bluedevil + ++ lib.optional config.networking.networkmanager.enable kde5.plasma-nm + ++ lib.optional config.hardware.pulseaudio.enable kde5.plasma-pa + ++ lib.optional config.powerManagement.enable kde5.powerdevil + ++ lib.optional config.services.colord.enable pkgs.colord-kde + ++ lib.optionals config.services.samba.enable [ kde5.kdenetwork-filesharing pkgs.samba ]; + + services.xserver.desktopManager.kde5.extraPackages = + [ + kde5.khelpcenter + kde5.oxygen + + kde5.dolphin + kde5.konsole + ]; + + environment.pathsToLink = [ "/share" ]; + + environment.etc = singleton { + source = "${pkgs.xkeyboard_config}/etc/X11/xkb"; + target = "X11/xkb"; + }; - # Enable GTK applications to load SVG icons - environment.variables = - { - GST_PLUGIN_SYSTEM_PATH_1_0 = - lib.makeSearchPath "/lib/gstreamer-1.0" - (builtins.map (pkg: pkg.out) (with pkgs.gst_all_1; [ - gstreamer - gst-plugins-base - gst-plugins-good - gst-plugins-ugly - gst-plugins-bad - gst-libav # for mp3 playback - ])); - } - // (if (lib.hasAttr "breeze-icons" kde5) - then { GDK_PIXBUF_MODULE_FILE = "${pkgs.librsvg.out}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache"; } - else { }); - - fonts.fonts = [ (kde5.oxygen-fonts or pkgs.noto-fonts) ]; - - programs.ssh.askPassword = "${kde5.ksshaskpass.out}/bin/ksshaskpass"; - - # Enable helpful DBus services. - services.udisks2.enable = true; - services.upower.enable = config.powerManagement.enable; - - # Extra UDEV rules used by Solid - services.udev.packages = [ - pkgs.libmtp - pkgs.media-player-info - ]; - - services.xserver.displayManager.sddm = { - theme = "breeze"; - themes = [ - kde5.ecm # for the setup-hook - kde5.plasma-workspace - kde5.breeze-icons - (kde5.oxygen-icons or kde5.oxygen-icons5) + environment.variables = + { + # Enable GTK applications to load SVG icons + GST_PLUGIN_SYSTEM_PATH_1_0 = + lib.makeSearchPath "/lib/gstreamer-1.0" + (builtins.map (pkg: pkg.out) (with pkgs.gst_all_1; [ + gstreamer + gst-plugins-base + gst-plugins-good + gst-plugins-ugly + gst-plugins-bad + gst-libav # for mp3 playback + ])); + } + // (if (lib.hasAttr "breeze-icons" kde5) + then { GDK_PIXBUF_MODULE_FILE = "${pkgs.librsvg.out}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache"; } + else { }); + + fonts.fonts = [ (kde5.oxygen-fonts or pkgs.noto-fonts) ]; + + programs.ssh.askPassword = "${kde5.ksshaskpass.out}/bin/ksshaskpass"; + + # Enable helpful DBus services. + services.udisks2.enable = true; + services.upower.enable = config.powerManagement.enable; + + # Extra UDEV rules used by Solid + services.udev.packages = [ + pkgs.libmtp + pkgs.media-player-info ]; - }; - security.pam.services.kde = { allowNullPassword = true; }; + services.xserver.displayManager.sddm = { + theme = "breeze"; + themes = [ + kde5.ecm # for the setup-hook + kde5.plasma-workspace + kde5.breeze-icons + ]; + }; - }; + security.pam.services.kde = { allowNullPassword = true; }; + + }) + ]; } diff --git a/nixos/modules/services/x11/desktop-managers/lumina.nix b/nixos/modules/services/x11/desktop-managers/lumina.nix new file mode 100644 index 00000000000..f0b31a2acb0 --- /dev/null +++ b/nixos/modules/services/x11/desktop-managers/lumina.nix @@ -0,0 +1,52 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + xcfg = config.services.xserver; + cfg = xcfg.desktopManager.lumina; + +in + +{ + options = { + + services.xserver.desktopManager.lumina.enable = mkOption { + type = types.bool; + default = false; + description = "Enable the Lumina desktop manager"; + }; + + }; + + + config = mkIf (xcfg.enable && cfg.enable) { + + services.xserver.desktopManager.session = singleton { + name = "lumina"; + start = '' + exec ${pkgs.lumina}/bin/start-lumina-desktop + ''; + }; + + environment.systemPackages = [ + pkgs.fluxbox + pkgs.kde5.kwindowsystem + pkgs.kde5.oxygen-icons5 + pkgs.lumina + pkgs.numlockx + pkgs.qt5.qtsvg + pkgs.xscreensaver + ]; + + # Link some extra directories in /run/current-system/software/share + environment.pathsToLink = [ + "/share/desktop-directories" + "/share/icons" + "/share/lumina" + "/share" + ]; + + }; +} diff --git a/nixos/modules/services/x11/desktop-managers/lxqt.nix b/nixos/modules/services/x11/desktop-managers/lxqt.nix new file mode 100644 index 00000000000..89ad2882363 --- /dev/null +++ b/nixos/modules/services/x11/desktop-managers/lxqt.nix @@ -0,0 +1,66 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + # Remove packages of ys from xs, based on their names + removePackagesByName = xs: ys: + let + pkgName = drv: (builtins.parseDrvName drv.name).name; + ysNames = map pkgName ys; + in + filter (x: !(builtins.elem (pkgName x) ysNames)) xs; + + xcfg = config.services.xserver; + cfg = xcfg.desktopManager.lxqt; + +in + +{ + options = { + + services.xserver.desktopManager.lxqt.enable = mkOption { + type = types.bool; + default = false; + description = "Enable the LXQt desktop manager"; + }; + + environment.lxqt.excludePackages = mkOption { + default = []; + example = literalExample "[ pkgs.lxqt.qterminal ]"; + type = types.listOf types.package; + description = "Which LXQt packages to exclude from the default environment"; + }; + + }; + + config = mkIf (xcfg.enable && cfg.enable) { + + services.xserver.desktopManager.session = singleton { + name = "lxqt"; + bgSupport = true; + start = '' + exec ${pkgs.lxqt.lxqt-common}/bin/startlxqt + ''; + }; + + environment.systemPackages = + pkgs.lxqt.preRequisitePackages ++ + pkgs.lxqt.corePackages ++ + (removePackagesByName + pkgs.lxqt.optionalPackages + config.environment.lxqt.excludePackages); + + # Link some extra directories in /run/current-system/software/share + environment.pathsToLink = [ + "/share/desktop-directories" + "/share/icons" + "/share/lxqt" + ]; + + environment.variables.GIO_EXTRA_MODULES = [ "${pkgs.gvfs}/lib/gio/modules" ]; + + }; + +} diff --git a/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixos/modules/services/x11/desktop-managers/xfce.nix index 634d2a39576..530468be5f9 100644 --- a/nixos/modules/services/x11/desktop-managers/xfce.nix +++ b/nixos/modules/services/x11/desktop-managers/xfce.nix @@ -69,7 +69,7 @@ in services.xserver.updateDbusEnvironment = true; environment.systemPackages = - [ pkgs.gtk # To get GTK+'s themes. + [ pkgs.gtk2.out # To get GTK+'s themes and gtk-update-icon-cache pkgs.hicolor_icon_theme pkgs.tango-icon-theme pkgs.shared_mime_info @@ -100,6 +100,7 @@ in pkgs.xfce.tumbler # found via dbus ] ++ optional config.powerManagement.enable pkgs.xfce.xfce4_power_manager + ++ optional config.networking.networkmanager.enable pkgs.networkmanagerapplet ++ optionals (!cfg.noDesktop) [ pkgs.xfce.xfce4panel pkgs.xfce.xfdesktop diff --git a/nixos/modules/services/x11/display-managers/default.nix b/nixos/modules/services/x11/display-managers/default.nix index 75d80609f73..c0daf30d04e 100644 --- a/nixos/modules/services/x11/display-managers/default.nix +++ b/nixos/modules/services/x11/display-managers/default.nix @@ -82,12 +82,12 @@ let # Speed up application start by 50-150ms according to # http://kdemonkey.blogspot.nl/2008/04/magic-trick.html - rm -rf $HOME/.compose-cache - mkdir $HOME/.compose-cache + rm -rf "$HOME/.compose-cache" + mkdir "$HOME/.compose-cache" # Work around KDE errors when a user first logs in and # .local/share doesn't exist yet. - mkdir -p $HOME/.local/share + mkdir -p "$HOME/.local/share" unset _DID_SYSTEMD_CAT @@ -134,13 +134,8 @@ let (*) echo "$0: Desktop manager '$desktopManager' not found.";; esac - # FIXME: gdbus should not be in glib.dev! - ${optionalString (cfg.startDbusSession && cfg.updateDbusEnvironment) '' - ${pkgs.glib.dev}/bin/gdbus call --session \ - --dest org.freedesktop.DBus --object-path /org/freedesktop/DBus \ - --method org.freedesktop.DBus.UpdateActivationEnvironment \ - "{$(env | ${pkgs.gnused}/bin/sed "s/'/\\\\'/g; s/\([^=]*\)=\(.*\)/'\1':'\2'/" \ - | ${pkgs.coreutils}/bin/paste -sd,)}" + ${optionalString cfg.updateDbusEnvironment '' + ${lib.getBin pkgs.dbus}/bin/dbus-update-activation-environment --systemd --all ''} test -n "$waitPID" && wait "$waitPID" @@ -153,7 +148,7 @@ let allowSubstitutes = false; } '' - mkdir -p $out + mkdir -p "$out" ${concatMapStrings (n: '' cat - > "$out/${n}.desktop" << EODESKTOP [Desktop Entry] @@ -192,7 +187,6 @@ in default = []; example = [ "-ac" "-logverbose" "-verbose" "-nolisten tcp" ]; description = "List of arguments for the X server."; - apply = toString; }; sessionCommands = mkOption { diff --git a/nixos/modules/services/x11/display-managers/gdm.nix b/nixos/modules/services/x11/display-managers/gdm.nix index 52847d2f8d2..6c63fede857 100644 --- a/nixos/modules/services/x11/display-managers/gdm.nix +++ b/nixos/modules/services/x11/display-managers/gdm.nix @@ -92,12 +92,16 @@ in users.extraGroups.gdm.gid = config.ids.gids.gdm; + # GDM needs different xserverArgs, presumable because using wayland by default. + services.xserver.tty = null; + services.xserver.display = null; + services.xserver.displayManager.job = { environment = { - GDM_X_SERVER = "${cfg.xserverBin} ${cfg.xserverArgs}"; + GDM_X_SERVER_EXTRA_ARGS = toString + (filter (arg: arg != "-terminate") cfg.xserverArgs); GDM_SESSIONS_DIR = "${cfg.session.desktops}"; - XDG_CONFIG_DIRS = "${gnome3.gnome_settings_daemon}/etc/xdg"; # Find the mouse XCURSOR_PATH = "~/.icons:${config.system.path}/share/icons"; }; @@ -108,10 +112,12 @@ in systemd.services.display-manager.wants = [ "systemd-machined.service" ]; systemd.services.display-manager.after = [ "systemd-machined.service" ]; - systemd.services.display-manager.path = [ gnome3.gnome_shell gnome3.caribou pkgs.xorg.xhost pkgs.dbus_tools ]; + systemd.services.display-manager.path = [ gnome3.gnome_session ]; services.dbus.packages = [ gdm ]; + systemd.user.services.dbus.wantedBy = [ "default.target" ]; + programs.dconf.profiles.gdm = "${gdm}/share/dconf/profile/gdm"; # Use AutomaticLogin if delay is zero, because it's immediate. diff --git a/nixos/modules/services/x11/display-managers/kdm.nix b/nixos/modules/services/x11/display-managers/kdm.nix index d9f7f8f0dfc..04701a1640c 100644 --- a/nixos/modules/services/x11/display-managers/kdm.nix +++ b/nixos/modules/services/x11/display-managers/kdm.nix @@ -25,7 +25,7 @@ let FailsafeClient=${pkgs.xterm}/bin/xterm [X-:*-Core] - ServerCmd=${dmcfg.xserverBin} ${dmcfg.xserverArgs} + ServerCmd=${dmcfg.xserverBin} ${toString dmcfg.xserverArgs} # KDM calls `rm' somewhere to clean up some temporary directory. SystemPath=${pkgs.coreutils}/bin # The default timeout (15) is too short in a heavily loaded boot process. @@ -54,19 +54,17 @@ let ''} ''; - kdmrc = pkgs.stdenv.mkDerivation { - name = "kdmrc"; - config = defaultConfig + cfg.extraConfig; - preferLocalBuild = true; - buildCommand = - '' - echo "$config" > $out + kdmrc = pkgs.runCommand "kdmrc" + { config = defaultConfig + cfg.extraConfig; + preferLocalBuild = true; + } + '' + echo "$config" > $out - # The default kdmrc would add "-nolisten tcp", and we already - # have that managed by nixos. Hence the grep. - cat ${kdebase_workspace}/share/config/kdm/kdmrc | grep -v nolisten >> $out - ''; - }; + # The default kdmrc would add "-nolisten tcp", and we already + # have that managed by nixos. Hence the grep. + cat ${kdebase_workspace}/share/config/kdm/kdmrc | grep -v nolisten >> $out + ''; in diff --git a/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix b/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix index 543dd628ce6..dfda90978b1 100644 --- a/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix +++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix @@ -16,11 +16,9 @@ let # The default greeter provided with this expression is the GTK greeter. # Again, we need a few things in the environment for the greeter to run with # fonts/icons. - wrappedGtkGreeter = stdenv.mkDerivation { - name = "lightdm-gtk-greeter"; - buildInputs = [ pkgs.makeWrapper ]; - - buildCommand = '' + wrappedGtkGreeter = pkgs.runCommand "lightdm-gtk-greeter" + { buildInputs = [ pkgs.makeWrapper ]; } + '' # This wrapper ensures that we actually get themes makeWrapper ${pkgs.lightdm_gtk_greeter}/sbin/lightdm-gtk-greeter \ $out/greeter \ @@ -40,7 +38,6 @@ let Type=Application EOF ''; - }; gtkGreeterConf = writeText "lightdm-gtk-greeter.conf" '' diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix index 47786f0a432..4afef32aaa4 100644 --- a/nixos/modules/services/x11/display-managers/lightdm.nix +++ b/nixos/modules/services/x11/display-managers/lightdm.nix @@ -23,7 +23,7 @@ let else additionalArgs="-logfile /var/log/X.$display.log" fi - exec ${dmcfg.xserverBin} ${dmcfg.xserverArgs} $additionalArgs "$@" + exec ${dmcfg.xserverBin} ${toString dmcfg.xserverArgs} $additionalArgs "$@" ''; usersConf = writeText "users.conf" @@ -46,13 +46,15 @@ let [Seat:*] xserver-command = ${xserverWrapper} session-wrapper = ${dmcfg.session.script} + ${optionalString (elem defaultSessionName dmcfg.session.names) '' + user-session = ${defaultSessionName} + ''} ${optionalString cfg.greeter.enable '' greeter-session = ${cfg.greeter.name} ''} ${optionalString cfg.autoLogin.enable '' autologin-user = ${cfg.autoLogin.user} autologin-user-timeout = ${toString cfg.autoLogin.timeout} - autologin-session = ${defaultSessionName} ''} ${cfg.extraSeatDefaults} ''; @@ -205,6 +207,9 @@ in services.dbus.enable = true; services.dbus.packages = [ lightdm ]; + # lightdm uses the accounts daemon to rember language/window-manager per user + services.accounts-daemon.enable = true; + security.pam.services.lightdm = { allowNullPassword = true; startSession = true; diff --git a/nixos/modules/services/x11/display-managers/sddm.nix b/nixos/modules/services/x11/display-managers/sddm.nix index 16d1e89e8d9..6630b8257e4 100644 --- a/nixos/modules/services/x11/display-managers/sddm.nix +++ b/nixos/modules/services/x11/display-managers/sddm.nix @@ -14,7 +14,7 @@ let xserverWrapper = pkgs.writeScript "xserver-wrapper" '' #!/bin/sh ${concatMapStrings (n: "export ${n}=\"${getAttr n xEnv}\"\n") (attrNames xEnv)} - exec ${dmcfg.xserverBin} ${dmcfg.xserverArgs} "$@" + exec systemd-cat ${dmcfg.xserverBin} ${toString dmcfg.xserverArgs} "$@" ''; Xsetup = pkgs.writeScript "Xsetup" '' @@ -27,7 +27,6 @@ let ${cfg.stopScript} ''; - cfgFile = pkgs.writeText "sddm.conf" '' [General] HaltCommand=${pkgs.systemd}/bin/systemctl poweroff @@ -46,8 +45,8 @@ let HideUsers=${concatStringsSep "," dmcfg.hiddenUsers} HideShells=/run/current-system/sw/bin/nologin - [XDisplay] - MinimumVT=${toString xcfg.tty} + [X11] + MinimumVT=${toString (if xcfg.tty != null then xcfg.tty else 7)} ServerPath=${xserverWrapper} XephyrPath=${pkgs.xorg.xorgserver.out}/bin/Xephyr SessionCommand=${dmcfg.session.script} @@ -86,7 +85,7 @@ in }; extraConfig = mkOption { - type = types.str; + type = types.lines; default = ""; example = '' [Autologin] @@ -100,7 +99,7 @@ in theme = mkOption { type = types.str; - default = "maui"; + default = ""; description = '' Greeter theme to use. ''; @@ -254,5 +253,10 @@ in users.extraGroups.sddm.gid = config.ids.gids.sddm; + services.dbus.packages = [ sddm.unwrapped ]; + + # To enable user switching, allow sddm to allocate TTYs/displays dynamically. + services.xserver.tty = null; + services.xserver.display = null; }; } diff --git a/nixos/modules/services/x11/display-managers/slim.nix b/nixos/modules/services/x11/display-managers/slim.nix index ce44c9f54a3..68acde85b5d 100644 --- a/nixos/modules/services/x11/display-managers/slim.nix +++ b/nixos/modules/services/x11/display-managers/slim.nix @@ -12,7 +12,7 @@ let '' xauth_path ${dmcfg.xauthBin} default_xserver ${dmcfg.xserverBin} - xserver_arguments ${dmcfg.xserverArgs} + xserver_arguments ${toString dmcfg.xserverArgs} sessiondir ${dmcfg.session.desktops} login_cmd exec ${pkgs.stdenv.shell} ${dmcfg.session.script} "%session" halt_cmd ${config.systemd.package}/sbin/shutdown -h now @@ -26,15 +26,13 @@ let # Unpack the SLiM theme, or use the default. slimThemesDir = let - unpackedTheme = pkgs.stdenv.mkDerivation { - name = "slim-theme"; - buildCommand = '' + unpackedTheme = pkgs.runCommand "slim-theme" {} + '' mkdir -p $out cd $out unpackFile ${cfg.theme} ln -s * default ''; - }; in if cfg.theme == null then "${pkgs.slim}/share/slim/themes" else unpackedTheme; in diff --git a/nixos/modules/services/x11/hardware/synaptics.nix b/nixos/modules/services/x11/hardware/synaptics.nix index 5c068e89dd7..2a7f4e5cbcd 100644 --- a/nixos/modules/services/x11/hardware/synaptics.nix +++ b/nixos/modules/services/x11/hardware/synaptics.nix @@ -19,7 +19,7 @@ let cfg = config.services.xserver.synaptics; Option "TapButton3" "0" ''; pkg = pkgs.xorg.xf86inputsynaptics; - etcFile = "X11/xorg.conf.d/50-synaptics.conf"; + etcFile = "X11/xorg.conf.d/70-synaptics.conf"; in { options = { @@ -172,7 +172,7 @@ in { services.xserver.modules = [ pkg.out ]; environment.etc."${etcFile}".source = - "${pkg.out}/share/X11/xorg.conf.d/50-synaptics.conf"; + "${pkg.out}/share/X11/xorg.conf.d/70-synaptics.conf"; environment.systemPackages = [ pkg ]; diff --git a/nixos/modules/services/x11/hardware/wacom.nix b/nixos/modules/services/x11/hardware/wacom.nix index 540ed168b48..a27889c36a7 100644 --- a/nixos/modules/services/x11/hardware/wacom.nix +++ b/nixos/modules/services/x11/hardware/wacom.nix @@ -22,7 +22,7 @@ in which will make Xorg reconfigure the device ? If you're not satisfied by the default behaviour you can override - <option>environment.etc."X11/xorg.conf.d/50-wacom.conf"</option> in + <option>environment.etc."X11/xorg.conf.d/70-wacom.conf"</option> in configuration.nix easily. ''; }; @@ -40,7 +40,7 @@ in services.udev.packages = [ pkgs.xf86_input_wacom ]; - environment.etc."X11/xorg.conf.d/50-wacom.conf".source = "${pkgs.xf86_input_wacom}/share/X11/xorg.conf.d/50-wacom.conf"; + environment.etc."X11/xorg.conf.d/70-wacom.conf".source = "${pkgs.xf86_input_wacom}/share/X11/xorg.conf.d/70-wacom.conf"; }; diff --git a/nixos/modules/services/x11/unclutter-xfixes.nix b/nixos/modules/services/x11/unclutter-xfixes.nix new file mode 100644 index 00000000000..bd02c5ed989 --- /dev/null +++ b/nixos/modules/services/x11/unclutter-xfixes.nix @@ -0,0 +1,58 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let cfg = config.services.unclutter-xfixes; + +in { + options.services.unclutter-xfixes = { + + enable = mkOption { + description = "Enable unclutter-xfixes to hide your mouse cursor when inactive."; + type = types.bool; + default = false; + example = true; + }; + + package = mkOption { + description = "unclutter-xfixes derivation to use."; + type = types.package; + default = pkgs.unclutter-xfixes; + defaultText = "pkgs.unclutter-xfixes"; + }; + + timeout = mkOption { + description = "Number of seconds before the cursor is marked inactive."; + type = types.int; + default = 1; + }; + + threshold = mkOption { + description = "Minimum number of pixels considered cursor movement."; + type = types.int; + default = 1; + }; + + extraOptions = mkOption { + description = "More arguments to pass to the unclutter-xfixes command."; + type = types.listOf types.str; + default = []; + example = [ "exclude-root" "ignore-scrolling" "fork" ]; + }; + }; + + config = mkIf cfg.enable { + systemd.user.services.unclutter-xfixes = { + description = "unclutter-xfixes"; + wantedBy = [ "graphical.target" ]; + serviceConfig.ExecStart = '' + ${cfg.package}/bin/unclutter \ + --timeout ${toString cfg.timeout} \ + --jitter ${toString (cfg.threshold - 1)} \ + ${concatMapStrings (x: " --"+x) cfg.extraOptions} \ + ''; + serviceConfig.RestartSec = 3; + serviceConfig.Restart = "always"; + }; + }; +} diff --git a/nixos/modules/services/x11/urxvtd.nix b/nixos/modules/services/x11/urxvtd.nix new file mode 100644 index 00000000000..be36efaa589 --- /dev/null +++ b/nixos/modules/services/x11/urxvtd.nix @@ -0,0 +1,50 @@ +{ config, lib, pkgs, ... }: + +# maintainer: siddharthist + +with lib; + +let + cfg = config.services.urxvtd; +in { + + options.services.urxvtd.enable = mkOption { + type = types.bool; + default = false; + example = true; + description = '' + Enable urxvtd, the urxvt terminal daemon. To use urxvtd, run + "urxvtc". + ''; + }; + + config = mkIf cfg.enable { + systemd.user = { + sockets.urxvtd = { + description = "socket for urxvtd, the urxvt terminal daemon"; + after = [ "graphical.target" ]; + wants = [ "graphical.target" ]; + wantedBy = [ "sockets.target" ]; + socketConfig = { + ListenStream = "%t/urxvtd-socket"; + }; + }; + + services.urxvtd = { + description = "urxvt terminal daemon"; + path = [ pkgs.xsel ]; + serviceConfig = { + ExecStart = "${pkgs.rxvt_unicode-with-plugins}/bin/urxvtd -o"; + Environment = "RXVT_SOCKET=%t/urxvtd-socket"; + Restart = "on-failure"; + RestartSec = "5s"; + }; + }; + + }; + + environment.systemPackages = [ pkgs.rxvt_unicode-with-plugins ]; + environment.variables.RXVT_SOCKET = "/run/user/$(id -u)/urxvtd-socket"; + }; + +} diff --git a/nixos/modules/services/x11/window-managers/awesome.nix b/nixos/modules/services/x11/window-managers/awesome.nix index 455b3568499..eb97449c6bd 100644 --- a/nixos/modules/services/x11/window-managers/awesome.nix +++ b/nixos/modules/services/x11/window-managers/awesome.nix @@ -6,7 +6,7 @@ let cfg = config.services.xserver.windowManager.awesome; awesome = cfg.package; - + inherit (pkgs.luaPackages) getLuaPath getLuaCPath; in { @@ -46,10 +46,8 @@ in { name = "awesome"; start = '' - ${concatMapStrings (pkg: '' - export LUA_CPATH=$LUA_CPATH''${LUA_CPATH:+;}${pkg}/lib/lua/${awesome.lua.luaversion}/?.so - export LUA_PATH=$LUA_PATH''${LUA_PATH:+;}${pkg}/lib/lua/${awesome.lua.luaversion}/?.lua - '') cfg.luaModules} + export LUA_CPATH="${lib.concatStringsSep ";" (map getLuaCPath cfg.luaModules)}" + export LUA_PATH="${lib.concatStringsSep ";" (map getLuaPath cfg.luaModules)}" ${awesome}/bin/awesome & waitPID=$! @@ -59,5 +57,4 @@ in environment.systemPackages = [ awesome ]; }; - } diff --git a/nixos/modules/services/x11/window-managers/bspwm.nix b/nixos/modules/services/x11/window-managers/bspwm.nix index 03a1b7a72e8..6783ac3479e 100644 --- a/nixos/modules/services/x11/window-managers/bspwm.nix +++ b/nixos/modules/services/x11/window-managers/bspwm.nix @@ -9,40 +9,69 @@ in { options = { services.xserver.windowManager.bspwm = { - enable = mkEnableOption "bspwm"; - startThroughSession = mkOption { - type = with types; bool; - default = false; - description = " - Start the window manager through the script defined in - sessionScript. Defaults to the the bspwm-session script - provided by bspwm - "; - }; - sessionScript = mkOption { - default = "${pkgs.bspwm}/bin/bspwm-session"; - defaultText = "(pkgs.bspwm)/bin/bspwm-session"; - description = " - The start-session script to use. Defaults to the - provided bspwm-session script from the bspwm package. + enable = mkEnableOption "bspwm"; + + package = mkOption { + type = types.package; + default = pkgs.bspwm; + defaultText = "pkgs.bspwm"; + example = "pkgs.bspwm-unstable"; + description = '' + bspwm package to use. + ''; + }; + configFile = mkOption { + type = with types; nullOr path; + example = "${pkgs.bspwm}/share/doc/bspwm/examples/bspwmrc"; + default = null; + description = '' + Path to the bspwm configuration file. + If null, $HOME/.config/bspwm/bspwmrc will be used. + ''; + }; - Does nothing unless `bspwm.startThroughSession` is enabled - "; + sxhkd = { + package = mkOption { + type = types.package; + default = pkgs.sxhkd; + defaultText = "pkgs.sxhkd"; + example = "pkgs.sxhkd-unstable"; + description = '' + sxhkd package to use. + ''; }; + configFile = mkOption { + type = with types; nullOr path; + example = "${pkgs.bspwm}/share/doc/bspwm/examples/sxhkdrc"; + default = null; + description = '' + Path to the sxhkd configuration file. + If null, $HOME/.config/sxhkd/sxhkdrc will be used. + ''; + }; + }; }; }; config = mkIf cfg.enable { services.xserver.windowManager.session = singleton { - name = "bspwm"; - start = if cfg.startThroughSession - then cfg.sessionScript - else '' - export _JAVA_AWT_WM_NONREPARENTING=1 - SXHKD_SHELL=/bin/sh ${pkgs.sxhkd}/bin/sxhkd -f 100 & - ${pkgs.bspwm}/bin/bspwm - ''; + name = "bspwm"; + start = '' + export _JAVA_AWT_WM_NONREPARENTING=1 + SXHKD_SHELL=/bin/sh ${cfg.sxhkd.package}/bin/sxhkd ${optionalString (cfg.sxhkd.configFile != null) "-c \"${cfg.sxhkd.configFile}\""} & + ${cfg.package}/bin/bspwm ${optionalString (cfg.configFile != null) "-c \"${cfg.configFile}\""} + waitPID=$! + ''; }; - environment.systemPackages = [ pkgs.bspwm ]; + environment.systemPackages = [ cfg.package ]; }; + + imports = [ + (mkRemovedOptionModule [ "services" "xserver" "windowManager" "bspwm-unstable" "enable" ] + "Use services.xserver.windowManager.bspwm.enable and set services.xserver.windowManager.bspwm.package to pkgs.bspwm-unstable to use the unstable version of bspwm.") + (mkRemovedOptionModule [ "services" "xserver" "windowManager" "bspwm" "startThroughSession" ] + "bspwm package does not provide bspwm-session anymore.") + (mkRemovedOptionModule [ "services" "xserver" "windowManager" "bspwm" "sessionScript" ] + "bspwm package does not provide bspwm-session anymore.") + ]; } diff --git a/nixos/modules/services/x11/window-managers/i3.nix b/nixos/modules/services/x11/window-managers/i3.nix index cfe9439b688..f9c75e80db4 100644 --- a/nixos/modules/services/x11/window-managers/i3.nix +++ b/nixos/modules/services/x11/window-managers/i3.nix @@ -3,52 +3,58 @@ with lib; let - wmCfg = config.services.xserver.windowManager; + cfg = config.services.xserver.windowManager.i3; +in + +{ + options.services.xserver.windowManager.i3 = { + enable = mkEnableOption "i3 window manager"; - i3option = name: { - enable = mkEnableOption name; configFile = mkOption { - default = null; - type = types.nullOr types.path; + default = null; + type = with types; nullOr path; description = '' Path to the i3 configuration file. If left at the default value, $HOME/.i3/config will be used. ''; }; + extraSessionCommands = mkOption { - default = ""; - type = types.lines; + default = ""; + type = types.lines; description = '' Shell commands executed just before i3 is started. ''; }; + + package = mkOption { + type = types.package; + default = pkgs.i3; + defaultText = "pkgs.i3"; + example = "pkgs.i3-gaps"; + description = '' + i3 package to use. + ''; + }; }; - i3config = name: pkg: cfg: { + config = mkIf cfg.enable { services.xserver.windowManager.session = [{ - inherit name; + name = "i3"; start = '' ${cfg.extraSessionCommands} - ${pkg}/bin/i3 ${optionalString (cfg.configFile != null) + ${cfg.package}/bin/i3 ${optionalString (cfg.configFile != null) "-c \"${cfg.configFile}\"" } & waitPID=$! ''; }]; - environment.systemPackages = [ pkg ]; - }; - -in - -{ - options.services.xserver.windowManager = { - i3 = i3option "i3"; - i3-gaps = i3option "i3-gaps"; + environment.systemPackages = [ cfg.package ]; }; - config = mkMerge [ - (mkIf wmCfg.i3.enable (i3config "i3" pkgs.i3 wmCfg.i3)) - (mkIf wmCfg.i3-gaps.enable (i3config "i3-gaps" pkgs.i3-gaps wmCfg.i3-gaps)) + imports = [ + (mkRemovedOptionModule [ "services" "xserver" "windowManager" "i3-gaps" "enable" ] + "Use services.xserver.windowManager.i3.enable and set services.xserver.windowManager.i3.package to pkgs.i3-gaps to use i3-gaps.") ]; } diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix index b03f70385b1..f5ed5233818 100644 --- a/nixos/modules/services/x11/xserver.nix +++ b/nixos/modules/services/x11/xserver.nix @@ -71,15 +71,11 @@ let monitors = reverseList (foldl mkMonitor [] xrandrHeads); in concatMapStrings (getAttr "value") monitors; - configFile = pkgs.stdenv.mkDerivation { - name = "xserver.conf"; - - xfs = optionalString (cfg.useXFS != false) - ''FontPath "${toString cfg.useXFS}"''; - - inherit (cfg) config; - - buildCommand = + configFile = pkgs.runCommand "xserver.conf" + { xfs = optionalString (cfg.useXFS != false) + ''FontPath "${toString cfg.useXFS}"''; + inherit (cfg) config; + } '' echo 'Section "Files"' >> $out echo $xfs >> $out @@ -102,7 +98,6 @@ let echo "$config" >> $out ''; # */ - }; in @@ -154,6 +149,22 @@ in ''; }; + autoRepeatDelay = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + Sets the autorepeat delay (length of time in milliseconds that a key must be depressed before autorepeat starts). + ''; + }; + + autoRepeatInterval = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + Sets the autorepeat interval (length of time in milliseconds that should elapse between autorepeat-generated keystrokes). + ''; + }; + inputClassSections = mkOption { type = types.listOf types.lines; default = []; @@ -504,12 +515,12 @@ in { description = "X11 Server"; after = [ "systemd-udev-settle.service" "local-fs.target" "acpid.service" "systemd-logind.service" ]; + wants = [ "systemd-udev-settle.service" ]; restartIfChanged = false; environment = { - XKB_BINDIR = "${xorg.xkbcomp}/bin"; # Needed for the Xkb extension. XORG_DRI_DRIVER_PATH = "/run/opengl-driver/lib/dri"; # !!! Depends on the driver selected at runtime. LD_LIBRARY_PATH = concatStringsSep ":" ( [ "${xorg.libX11.out}/lib" "${xorg.libXext.out}/lib" "/run/opengl-driver/lib" ] @@ -529,6 +540,10 @@ in Restart = "always"; RestartSec = "200ms"; SyslogIdentifier = "display-manager"; + # Stop restarting if the display manager stops (crashes) 2 times + # in one minute. Starting X typically takes 3-4s. + StartLimitInterval = "30s"; + StartLimitBurst = "3"; }; }; @@ -541,7 +556,9 @@ in ] ++ optional (cfg.display != null) ":${toString cfg.display}" ++ optional (cfg.tty != null) "vt${toString cfg.tty}" ++ optional (cfg.dpi != null) "-dpi ${toString cfg.dpi}" - ++ optional (!cfg.enableTCP) "-nolisten tcp"; + ++ optional (!cfg.enableTCP) "-nolisten tcp" + ++ optional (cfg.autoRepeatDelay != null) "-ardelay ${toString cfg.autoRepeatDelay}" + ++ optional (cfg.autoRepeatInterval != null) "-arinterval ${toString cfg.autoRepeatInterval}"; services.xserver.modules = concatLists (catAttrs "modules" cfg.drivers) ++ @@ -654,6 +671,8 @@ in ${xrandrMonitorSections} ''; + fonts.enableDefaultFonts = mkDefault true; + }; } |