diff options
Diffstat (limited to 'nixos/modules/services')
47 files changed, 1565 insertions, 846 deletions
diff --git a/nixos/modules/services/backup/tsm.nix b/nixos/modules/services/backup/tsm.nix new file mode 100644 index 00000000000..3b2bb37491b --- /dev/null +++ b/nixos/modules/services/backup/tsm.nix @@ -0,0 +1,106 @@ +{ config, lib, ... }: + +let + + inherit (lib.attrsets) hasAttr; + inherit (lib.modules) mkDefault mkIf; + inherit (lib.options) mkEnableOption mkOption; + inherit (lib.types) nullOr strMatching; + + options.services.tsmBackup = { + enable = mkEnableOption '' + automatic backups with the + IBM Spectrum Protect (Tivoli Storage Manager, TSM) client. + This also enables + <option>programs.tsmClient.enable</option> + ''; + command = mkOption { + type = strMatching ".+"; + default = "backup"; + example = "incr"; + description = '' + The actual command passed to the + <literal>dsmc</literal> executable to start the backup. + ''; + }; + servername = mkOption { + type = strMatching ".+"; + example = "mainTsmServer"; + description = '' + Create a systemd system service + <literal>tsm-backup.service</literal> that starts + a backup based on the given servername's stanza. + Note that this server's + <option>passwdDir</option> will default to + <filename>/var/lib/tsm-backup/password</filename> + (but may be overridden); + also, the service will use + <filename>/var/lib/tsm-backup</filename> as + <literal>HOME</literal> when calling + <literal>dsmc</literal>. + ''; + }; + autoTime = mkOption { + type = nullOr (strMatching ".+"); + default = null; + example = "12:00"; + description = '' + The backup service will be invoked + automatically at the given date/time, + which must be in the format described in + <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>5</manvolnum></citerefentry>. + The default <literal>null</literal> + disables automatic backups. + ''; + }; + }; + + cfg = config.services.tsmBackup; + cfgPrg = config.programs.tsmClient; + + assertions = [ + { + assertion = hasAttr cfg.servername cfgPrg.servers; + message = "TSM service servername not found in list of servers"; + } + { + assertion = cfgPrg.servers.${cfg.servername}.genPasswd; + message = "TSM service requires automatic password generation"; + } + ]; + +in + +{ + + inherit options; + + config = mkIf cfg.enable { + inherit assertions; + programs.tsmClient.enable = true; + programs.tsmClient.servers."${cfg.servername}".passwdDir = + mkDefault "/var/lib/tsm-backup/password"; + systemd.services.tsm-backup = { + description = "IBM Spectrum Protect (Tivoli Storage Manager) Backup"; + # DSM_LOG needs a trailing slash to have it treated as a directory. + # `/var/log` would be littered with TSM log files otherwise. + environment.DSM_LOG = "/var/log/tsm-backup/"; + # TSM needs a HOME dir to store certificates. + environment.HOME = "/var/lib/tsm-backup"; + # for exit status description see + # https://www.ibm.com/support/knowledgecenter/en/SSEQVQ_8.1.8/client/c_sched_rtncode.html + serviceConfig.SuccessExitStatus = "4 8"; + # The `-se` option must come after the command. + # The `-optfile` option suppresses a `dsm.opt`-not-found warning. + serviceConfig.ExecStart = + "${cfgPrg.wrappedPackage}/bin/dsmc ${cfg.command} -se='${cfg.servername}' -optfile=/dev/null"; + serviceConfig.LogsDirectory = "tsm-backup"; + serviceConfig.StateDirectory = "tsm-backup"; + serviceConfig.StateDirectoryMode = "0750"; + startAt = mkIf (cfg.autoTime!=null) cfg.autoTime; + }; + }; + + meta.maintainers = [ lib.maintainers.yarny ]; + +} diff --git a/nixos/modules/services/databases/foundationdb.nix b/nixos/modules/services/databases/foundationdb.nix index 6182da5e7d6..3746b875c7f 100644 --- a/nixos/modules/services/databases/foundationdb.nix +++ b/nixos/modules/services/databases/foundationdb.nix @@ -363,7 +363,7 @@ in "d /etc/foundationdb 0755 ${cfg.user} ${cfg.group} - -" "d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group} - -" "d '${cfg.logDir}' 0770 ${cfg.user} ${cfg.group} - -" - "F '${cfg.pidFile}' - ${cfg.user} ${cfg.group} - -" + "F '${cfg.pidfile}' - ${cfg.user} ${cfg.group} - -" ]; systemd.services.foundationdb = { diff --git a/nixos/modules/services/databases/openldap.nix b/nixos/modules/services/databases/openldap.nix index c2f458c0379..d8e2c715afb 100644 --- a/nixos/modules/services/databases/openldap.nix +++ b/nixos/modules/services/databases/openldap.nix @@ -237,8 +237,8 @@ in config = mkIf cfg.enable { assertions = [ { - assertion = cfg.rootpwFile != null || cfg.rootpw != null; - message = "Either services.openldap.rootpw or services.openldap.rootpwFile must be set"; + assertion = cfg.configDir != null || cfg.rootpwFile != null || cfg.rootpw != null; + message = "services.openldap: Unless configDir is set, either rootpw or rootpwFile must be set"; } ]; diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix index 5661edbee2d..37d44e30fbe 100644 --- a/nixos/modules/services/databases/postgresql.nix +++ b/nixos/modules/services/databases/postgresql.nix @@ -6,23 +6,10 @@ let cfg = config.services.postgresql; - # see description of extraPlugins - postgresqlAndPlugins = pg: - if cfg.extraPlugins == [] then pg - else pkgs.buildEnv { - name = "postgresql-and-plugins-${(builtins.parseDrvName pg.name).version}"; - 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 - ''; - }; - - postgresql = postgresqlAndPlugins cfg.package; + postgresql = + if cfg.extraPlugins == [] + then cfg.package + else cfg.package.withPackages (_: cfg.extraPlugins); # The main PostgreSQL configuration file. configFile = pkgs.writeText "postgresql.conf" @@ -55,7 +42,7 @@ in package = mkOption { type = types.package; - example = literalExample "pkgs.postgresql_9_6"; + example = literalExample "pkgs.postgresql_11"; description = '' PostgreSQL package to use. ''; @@ -71,7 +58,7 @@ in dataDir = mkOption { type = types.path; - example = "/var/lib/postgresql/9.6"; + example = "/var/lib/postgresql/11"; description = '' Data directory for PostgreSQL. ''; @@ -192,17 +179,11 @@ in extraPlugins = mkOption { type = types.listOf types.path; default = []; - example = literalExample "[ (pkgs.postgis.override { postgresql = pkgs.postgresql_9_4; }) ]"; + example = literalExample "with pkgs.postgresql_11.pkgs; [ postgis pg_repack ]"; description = '' - When this list contains elements a new store path is created. - PostgreSQL and the elements are symlinked into it. Then pg_config, - postgres and pg_ctl are copied to make them use the new - $out/lib directory as pkglibdir. This makes it possible to use postgis - without patching the .sql files which reference $libdir/postgis-1.5. + List of PostgreSQL plugins. PostgreSQL version for each plugin should + match version for <literal>services.postgresql.package</literal> value. ''; - # Note: the duplication of executables is about 4MB size. - # So a nicer solution was patching postgresql to allow setting the - # libdir explicitely. }; extraConfig = mkOption { diff --git a/nixos/modules/services/databases/postgresql.xml b/nixos/modules/services/databases/postgresql.xml index 00bb02dcc5b..02db47568d3 100644 --- a/nixos/modules/services/databases/postgresql.xml +++ b/nixos/modules/services/databases/postgresql.xml @@ -27,10 +27,10 @@ <filename>configuration.nix</filename>: <programlisting> <xref linkend="opt-services.postgresql.enable"/> = true; -<xref linkend="opt-services.postgresql.package"/> = pkgs.postgresql_9_4; +<xref linkend="opt-services.postgresql.package"/> = pkgs.postgresql_11; </programlisting> Note that you are required to specify the desired version of PostgreSQL - (e.g. <literal>pkgs.postgresql_9_4</literal>). Since upgrading your + (e.g. <literal>pkgs.postgresql_11</literal>). Since upgrading your PostgreSQL version requires a database dump and reload (see below), NixOS cannot provide a default value for <xref linkend="opt-services.postgresql.package"/> such as the most recent @@ -74,4 +74,70 @@ Type "help" for help. <link linkend="opt-services.postgresql.enable">here</link>. </para> </section> + <section xml:id="module-services-postgres-plugins"> + <title>Plugins</title> + + <para> + Plugins collection for each PostgreSQL version can be accessed with + <literal>.pkgs</literal>. For example, for + <literal>pkgs.postgresql_11</literal> package, its plugin collection is + accessed by <literal>pkgs.postgresql_11.pkgs</literal>: +<screen> +<prompt>$ </prompt>nix repl '<nixpkgs>' + +Loading '<nixpkgs>'... +Added 10574 variables. + +<prompt>nix-repl> </prompt>postgresql_11.pkgs.<TAB><TAB> +postgresql_11.pkgs.cstore_fdw postgresql_11.pkgs.pg_repack +postgresql_11.pkgs.pg_auto_failover postgresql_11.pkgs.pg_safeupdate +postgresql_11.pkgs.pg_bigm postgresql_11.pkgs.pg_similarity +postgresql_11.pkgs.pg_cron postgresql_11.pkgs.pg_topn +postgresql_11.pkgs.pg_hll postgresql_11.pkgs.pgjwt +postgresql_11.pkgs.pg_partman postgresql_11.pkgs.pgroonga +... +</screen> + </para> + <para> + To add plugins via NixOS configuration, set <literal>services.postgresql.extraPlugins</literal>: +<programlisting> +<xref linkend="opt-services.postgresql.package"/> = pkgs.postgresql_11; +<xref linkend="opt-services.postgresql.extraPlugins"/> = with pkgs.postgresql_11.pkgs; [ + pg_repack + postgis +]; +</programlisting> + </para> + <para> + You can build custom PostgreSQL-with-plugins (to be used outside of NixOS) using + function <literal>.withPackages</literal>. For example, creating a custom + PostgreSQL package in an overlay can look like: +<programlisting> +self: super: { + postgresql_custom = self.postgresql_11.withPackages (ps: [ + ps.pg_repack + ps.postgis + ]); +} +</programlisting> + </para> + <para> + Here's a recipe on how to override a particular plugin through an overlay: +<programlisting> +self: super: { + postgresql_11 = super.postgresql_11.override { this = self.postgresql_11; } // { + pkgs = super.postgresql_11.pkgs // { + pg_repack = super.postgresql_11.pkgs.pg_repack.overrideAttrs (_: { + name = "pg_repack-v20181024"; + src = self.fetchzip { + url = "https://github.com/reorg/pg_repack/archive/923fa2f3c709a506e111cc963034bf2fd127aa00.tar.gz"; + sha256 = "17k6hq9xaax87yz79j773qyigm4fwk8z4zh5cyp6z0sxnwfqxxw5"; + }; + }); + }; + }; +} +</programlisting> + </para> + </section> </chapter> diff --git a/nixos/modules/services/desktops/flatpak.nix b/nixos/modules/services/desktops/flatpak.nix index cfca1893bd8..82463406118 100644 --- a/nixos/modules/services/desktops/flatpak.nix +++ b/nixos/modules/services/desktops/flatpak.nix @@ -15,38 +15,22 @@ in { options = { services.flatpak = { enable = mkEnableOption "flatpak"; - - extraPortals = mkOption { - type = types.listOf types.package; - default = []; - description = '' - List of additional portals to add to path. Portals allow interaction - with system, like choosing files or taking screenshots. At minimum, - a desktop portal implementation should be listed. GNOME already - adds <package>xdg-desktop-portal-gtk</package>; for KDE, there - is <package>xdg-desktop-portal-kde</package>. Other desktop - environments will probably want to do the same. - ''; - }; }; }; ###### implementation config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.flatpak ]; - services.dbus.packages = [ pkgs.flatpak pkgs.xdg-desktop-portal ] ++ cfg.extraPortals; + services.dbus.packages = [ pkgs.flatpak ]; - systemd.packages = [ pkgs.flatpak pkgs.xdg-desktop-portal ] ++ cfg.extraPortals; + systemd.packages = [ pkgs.flatpak ]; environment.profiles = [ "$HOME/.local/share/flatpak/exports" "/var/lib/flatpak/exports" ]; - - environment.variables = { - XDG_DESKTOP_PORTAL_PATH = map (p: "${p}/share/xdg-desktop-portal/portals") cfg.extraPortals; - }; }; } diff --git a/nixos/modules/services/desktops/flatpak.xml b/nixos/modules/services/desktops/flatpak.xml index fb27bd1f62b..8f080b25022 100644 --- a/nixos/modules/services/desktops/flatpak.xml +++ b/nixos/modules/services/desktops/flatpak.xml @@ -29,7 +29,7 @@ in other cases, you will need to add something like the following to your <filename>configuration.nix</filename>: <programlisting> - <xref linkend="opt-services.flatpak.extraPortals"/> = [ pkgs.xdg-desktop-portal-gtk ]; + <xref linkend="opt-xdg.portal.extraPortals"/> = [ pkgs.xdg-desktop-portal-gtk ]; </programlisting> </para> <para> diff --git a/nixos/modules/services/desktops/tumbler.nix b/nixos/modules/services/desktops/tumbler.nix index ccbb6d1434d..d18088d4634 100644 --- a/nixos/modules/services/desktops/tumbler.nix +++ b/nixos/modules/services/desktops/tumbler.nix @@ -23,7 +23,7 @@ in package = mkOption { type = types.package; - default = pkgs.xfce4-13.tumbler; + default = pkgs.xfce4-14.tumbler; description = "Which tumbler package to use"; example = pkgs.xfce4-12.tumbler; }; diff --git a/nixos/modules/services/editors/emacs.xml b/nixos/modules/services/editors/emacs.xml index 88d7c4e1daf..a3041ae22e7 100644 --- a/nixos/modules/services/editors/emacs.xml +++ b/nixos/modules/services/editors/emacs.xml @@ -95,7 +95,8 @@ also available in Nixpkgs: <link xlink:href="https://www.gnu.org/software/zile/">Zile</link>, <link xlink:href="http://homepage.boetes.org/software/mg/">mg</link>, - <link xlink:href="http://yi-editor.github.io/">Yi</link>. + <link xlink:href="http://yi-editor.github.io/">Yi</link>, + <link xlink:href="https://joe-editor.sourceforge.io/">jmacs</link>. </para> </section> diff --git a/nixos/modules/services/hardware/sane_extra_backends/brscan4_etc_files.nix b/nixos/modules/services/hardware/sane_extra_backends/brscan4_etc_files.nix index bd114f0d2cc..fd19d8020fb 100644 --- a/nixos/modules/services/hardware/sane_extra_backends/brscan4_etc_files.nix +++ b/nixos/modules/services/hardware/sane_extra_backends/brscan4_etc_files.nix @@ -19,7 +19,7 @@ nix-shell -E 'with import <nixpkgs> { }; brscan4-etc-files.override{netDevices=[ */ -with lib; +with lib; let @@ -40,7 +40,7 @@ stdenv.mkDerivation rec { nativeBuildInputs = [ brscan4 ]; - configurePhase = ":"; + dontConfigure = true; buildPhase = '' TARGET_DIR="$out/etc/opt/brother/scanner/brscan4" diff --git a/nixos/modules/services/logging/graylog.nix b/nixos/modules/services/logging/graylog.nix index 49f3187fd31..a889a44d4b2 100644 --- a/nixos/modules/services/logging/graylog.nix +++ b/nixos/modules/services/logging/graylog.nix @@ -108,7 +108,7 @@ in }; extraConfig = mkOption { - type = types.str; + type = types.lines; default = ""; description = "Any other configuration options you might want to add"; }; diff --git a/nixos/modules/services/mail/postfix.nix b/nixos/modules/services/mail/postfix.nix index d43733484ff..dab1b29aa4b 100644 --- a/nixos/modules/services/mail/postfix.nix +++ b/nixos/modules/services/mail/postfix.nix @@ -13,6 +13,7 @@ let || cfg.extraAliases != ""; haveTransport = cfg.transport != ""; haveVirtual = cfg.virtual != ""; + haveLocalRecipients = cfg.localRecipients != null; clientAccess = optional (cfg.dnsBlacklistOverrides != "") @@ -244,6 +245,7 @@ let aliasesFile = pkgs.writeText "postfix-aliases" aliases; virtualFile = pkgs.writeText "postfix-virtual" cfg.virtual; + localRecipientMapFile = pkgs.writeText "postfix-local-recipient-map" (concatMapStrings (x: x + " ACCEPT\n") cfg.localRecipients); checkClientAccessFile = pkgs.writeText "postfix-check-client-access" cfg.dnsBlacklistOverrides; mainCfFile = pkgs.writeText "postfix-main.cf" mainCf; masterCfFile = pkgs.writeText "postfix-master.cf" masterCfContent; @@ -506,6 +508,19 @@ in ''; }; + localRecipients = mkOption { + type = with types; nullOr (listOf string); + default = null; + description = '' + List of accepted local users. Specify a bare username, an + <literal>"@domain.tld"</literal> wild-card, or a complete + <literal>"user@domain.tld"</literal> address. If set, these names end + up in the local recipient map -- see the local(8) man-page -- and + effectively replace the system user database lookup that's otherwise + used by default. + ''; + }; + transport = mkOption { default = ""; description = " @@ -742,6 +757,7 @@ in // optionalAttrs haveAliases { alias_maps = [ "${cfg.aliasMapType}:/etc/postfix/aliases" ]; } // optionalAttrs haveTransport { transport_maps = [ "hash:/etc/postfix/transport" ]; } // optionalAttrs haveVirtual { virtual_alias_maps = [ "${cfg.virtualMapType}:/etc/postfix/virtual" ]; } + // optionalAttrs haveLocalRecipients { local_recipient_maps = [ "hash:/etc/postfix/local_recipients" ] ++ optional haveAliases "$alias_maps"; } // optionalAttrs (cfg.dnsBlacklists != []) { smtpd_client_restrictions = clientRestrictions; } // optionalAttrs cfg.useSrs { sender_canonical_maps = [ "tcp:127.0.0.1:10001" ]; @@ -869,6 +885,9 @@ in (mkIf haveVirtual { services.postfix.mapFiles."virtual" = virtualFile; }) + (mkIf haveLocalRecipients { + services.postfix.mapFiles."local_recipients" = localRecipientMapFile; + }) (mkIf cfg.enableHeaderChecks { services.postfix.mapFiles."header_checks" = headerChecksFile; }) diff --git a/nixos/modules/services/misc/gitea.nix b/nixos/modules/services/misc/gitea.nix index 5a964e672ed..59c1c104b9b 100644 --- a/nixos/modules/services/misc/gitea.nix +++ b/nixos/modules/services/misc/gitea.nix @@ -55,6 +55,11 @@ let [service] DISABLE_REGISTRATION = ${boolToString cfg.disableRegistration} + ${optionalString (cfg.mailerPasswordFile != null) '' + [mailer] + PASSWD = #mailerpass# + ''} + ${cfg.extraConfig} ''; in @@ -255,6 +260,13 @@ in description = "Upper level of template and static files path."; }; + mailerPasswordFile = mkOption { + type = types.nullOr types.str; + default = null; + example = "/var/lib/secrets/gitea/mailpw"; + description = "Path to a file containing the SMTP password."; + }; + disableRegistration = mkEnableOption "the registration lock" // { description = '' By default any user can create an account on this <literal>gitea</literal> instance. @@ -344,9 +356,15 @@ in KEY="$(head -n1 ${secretKey})" DBPASS="$(head -n1 ${cfg.database.passwordFile})" JWTSECRET="$(head -n1 ${jwtSecret})" + ${if (cfg.mailerPasswordFile == null) then '' + MAILERPASSWORD="#mailerpass#" + '' else '' + MAILERPASSWORD="$(head -n1 ${cfg.mailerPasswordFile} || :)" + ''} sed -e "s,#secretkey#,$KEY,g" \ -e "s,#dbpass#,$DBPASS,g" \ -e "s,#jwtsecet#,$JWTSECET,g" \ + -e "s,#mailerpass#,$MAILERPASSWORD,g" \ -i ${runConfig} chmod 640 ${runConfig} ${secretKey} ${jwtSecret} ''} diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix index 71277b48ecd..52589b593b4 100644 --- a/nixos/modules/services/misc/gitlab.nix +++ b/nixos/modules/services/misc/gitlab.nix @@ -52,7 +52,7 @@ let gitlab_url = "http+unix://${pathUrlQuote gitlabSocket}"; http_settings.self_signed_cert = false; repos_path = "${cfg.statePath}/repositories"; - secret_file = "${cfg.statePath}/config/gitlab_shell_secret"; + secret_file = "${cfg.statePath}/gitlab_shell_secret"; log_file = "${cfg.statePath}/log/gitlab-shell.log"; custom_hooks_dir = "${cfg.statePath}/custom_hooks"; redis = { @@ -109,7 +109,7 @@ let gitlab_shell = { path = "${cfg.packages.gitlab-shell}"; hooks_path = "${cfg.statePath}/shell/hooks"; - secret_file = "${cfg.statePath}/config/gitlab_shell_secret"; + secret_file = "${cfg.statePath}/gitlab_shell_secret"; upload_pack = true; receive_pack = true; }; @@ -132,14 +132,9 @@ let HOME = "${cfg.statePath}/home"; UNICORN_PATH = "${cfg.statePath}/"; GITLAB_PATH = "${cfg.packages.gitlab}/share/gitlab/"; - GITLAB_STATE_PATH = cfg.statePath; - GITLAB_UPLOADS_PATH = "${cfg.statePath}/uploads"; SCHEMA = "${cfg.statePath}/db/schema.rb"; + GITLAB_UPLOADS_PATH = "${cfg.statePath}/uploads"; GITLAB_LOG_PATH = "${cfg.statePath}/log"; - GITLAB_SHELL_PATH = "${cfg.packages.gitlab-shell}"; - GITLAB_SHELL_CONFIG_PATH = "${cfg.statePath}/shell/config.yml"; - GITLAB_SHELL_SECRET_PATH = "${cfg.statePath}/config/gitlab_shell_secret"; - GITLAB_SHELL_HOOKS_PATH = "${cfg.statePath}/shell/hooks"; GITLAB_REDIS_CONFIG_FILE = pkgs.writeText "redis.yml" (builtins.toJSON redisConfig); prometheus_multiproc_dir = "/run/gitlab"; RAILS_ENV = "production"; @@ -149,7 +144,7 @@ let name = "gitlab-rake"; buildInputs = [ pkgs.makeWrapper ]; dontBuild = true; - unpackPhase = ":"; + dontUnpack = true; installPhase = '' mkdir -p $out/bin makeWrapper ${cfg.packages.gitlab.rubyEnv}/bin/rake $out/bin/gitlab-rake \ @@ -164,7 +159,7 @@ let name = "gitlab-rails"; buildInputs = [ pkgs.makeWrapper ]; dontBuild = true; - unpackPhase = ":"; + dontUnpack = true; installPhase = '' mkdir -p $out/bin makeWrapper ${cfg.packages.gitlab.rubyEnv}/bin/rails $out/bin/gitlab-rails \ @@ -502,23 +497,43 @@ in { systemd.tmpfiles.rules = [ "d /run/gitlab 0755 ${cfg.user} ${cfg.group} -" "d ${gitlabEnv.HOME} 0750 ${cfg.user} ${cfg.group} -" + "z ${gitlabEnv.HOME}/.ssh/authorized_keys 0600 ${cfg.user} ${cfg.group} -" "d ${cfg.backupPath} 0750 ${cfg.user} ${cfg.group} -" + "d ${cfg.statePath} 0750 ${cfg.user} ${cfg.group} -" "d ${cfg.statePath}/builds 0750 ${cfg.user} ${cfg.group} -" "d ${cfg.statePath}/config 0750 ${cfg.user} ${cfg.group} -" "d ${cfg.statePath}/db 0750 ${cfg.user} ${cfg.group} -" "d ${cfg.statePath}/log 0750 ${cfg.user} ${cfg.group} -" "d ${cfg.statePath}/repositories 2770 ${cfg.user} ${cfg.group} -" "d ${cfg.statePath}/shell 0750 ${cfg.user} ${cfg.group} -" + "d ${cfg.statePath}/tmp 0750 ${cfg.user} ${cfg.group} -" "d ${cfg.statePath}/tmp/pids 0750 ${cfg.user} ${cfg.group} -" "d ${cfg.statePath}/tmp/sockets 0750 ${cfg.user} ${cfg.group} -" "d ${cfg.statePath}/uploads 0700 ${cfg.user} ${cfg.group} -" + "d ${cfg.statePath}/custom_hooks 0700 ${cfg.user} ${cfg.group} -" "d ${cfg.statePath}/custom_hooks/pre-receive.d 0700 ${cfg.user} ${cfg.group} -" "d ${cfg.statePath}/custom_hooks/post-receive.d 0700 ${cfg.user} ${cfg.group} -" "d ${cfg.statePath}/custom_hooks/update.d 0700 ${cfg.user} ${cfg.group} -" + "d ${gitlabConfig.production.shared.path} 0750 ${cfg.user} ${cfg.group} -" "d ${gitlabConfig.production.shared.path}/artifacts 0750 ${cfg.user} ${cfg.group} -" "d ${gitlabConfig.production.shared.path}/lfs-objects 0750 ${cfg.user} ${cfg.group} -" "d ${gitlabConfig.production.shared.path}/pages 0750 ${cfg.user} ${cfg.group} -" - ]; + "L+ ${cfg.statePath}/lib - - - - ${cfg.packages.gitlab}/share/gitlab/lib" + "L+ /run/gitlab/config - - - - ${cfg.statePath}/config" + "L+ /run/gitlab/log - - - - ${cfg.statePath}/log" + "L+ /run/gitlab/tmp - - - - ${cfg.statePath}/tmp" + "L+ /run/gitlab/uploads - - - - ${cfg.statePath}/uploads" + + "L+ /run/gitlab/shell-config.yml - - - - ${pkgs.writeText "config.yml" (builtins.toJSON gitlabShellConfig)}" + + "L+ ${cfg.statePath}/config/gitlab.yml - - - - ${pkgs.writeText "gitlab.yml" (builtins.toJSON gitlabConfig)}" + "L+ ${cfg.statePath}/config/database.yml - - - - ${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)}" + "L+ ${cfg.statePath}/config/secrets.yml - - - - ${pkgs.writeText "secrets.yml" (builtins.toJSON secretsConfig)}" + "L+ ${cfg.statePath}/config/unicorn.rb - - - - ${./defaultUnicornConfig.rb}" + + "L+ ${cfg.statePath}/config/initializers/extra-gitlab.rb - - - - ${extraGitlabRb}" + ] ++ optional cfg.smtp.enable + "L+ ${cfg.statePath}/config/initializers/smtp_settings.rb - - - - ${smtpSettings}" ; systemd.services.gitlab-sidekiq = { after = [ "network.target" "redis.service" "gitlab.service" ]; @@ -609,40 +624,14 @@ in { gnupg ]; preStart = '' - cp -rf ${cfg.packages.gitlab}/share/gitlab/db/* ${cfg.statePath}/db - rm -rf ${cfg.statePath}/config - mkdir ${cfg.statePath}/config - if [ -e ${cfg.statePath}/lib ]; then - rm ${cfg.statePath}/lib - fi + ${pkgs.sudo}/bin/sudo -u ${cfg.user} cp -f ${cfg.packages.gitlab}/share/gitlab/VERSION ${cfg.statePath}/VERSION + ${pkgs.sudo}/bin/sudo -u ${cfg.user} rm -rf ${cfg.statePath}/db/* + ${pkgs.sudo}/bin/sudo -u ${cfg.user} cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config + ${pkgs.sudo}/bin/sudo -u ${cfg.user} cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/db/* ${cfg.statePath}/db - ln -sf ${cfg.packages.gitlab}/share/gitlab/lib ${cfg.statePath}/lib - [ -L /run/gitlab/config ] || ln -sf ${cfg.statePath}/config /run/gitlab/config - [ -L /run/gitlab/log ] || ln -sf ${cfg.statePath}/log /run/gitlab/log - [ -L /run/gitlab/tmp ] || ln -sf ${cfg.statePath}/tmp /run/gitlab/tmp - [ -L /run/gitlab/uploads ] || ln -sf ${cfg.statePath}/uploads /run/gitlab/uploads - cp ${cfg.packages.gitlab}/share/gitlab/VERSION ${cfg.statePath}/VERSION - cp -rf ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config - ln -sf ${extraGitlabRb} ${cfg.statePath}/config/initializers/extra-gitlab.rb - ${optionalString cfg.smtp.enable '' - ln -sf ${smtpSettings} ${cfg.statePath}/config/initializers/smtp_settings.rb - ''} - ${pkgs.openssl}/bin/openssl rand -hex 32 > ${cfg.statePath}/config/gitlab_shell_secret - - # JSON is a subset of YAML - ln -sf ${pkgs.writeText "gitlab.yml" (builtins.toJSON gitlabConfig)} ${cfg.statePath}/config/gitlab.yml - ln -sf ${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)} ${cfg.statePath}/config/database.yml - ln -sf ${pkgs.writeText "secrets.yml" (builtins.toJSON secretsConfig)} ${cfg.statePath}/config/secrets.yml - ln -sf ${./defaultUnicornConfig.rb} ${cfg.statePath}/config/unicorn.rb - - # Install the shell required to push repositories - ln -sf ${pkgs.writeText "config.yml" (builtins.toJSON gitlabShellConfig)} /run/gitlab/shell-config.yml - [ -L ${cfg.statePath}/shell/hooks ] || ln -sf ${cfg.packages.gitlab-shell}/hooks ${cfg.statePath}/shell/hooks - ${cfg.packages.gitlab-shell}/bin/install - - chown -R ${cfg.user}:${cfg.group} ${cfg.statePath}/ - chmod -R ug+rwX,o-rwx+X ${cfg.statePath}/ - chown -R ${cfg.user}:${cfg.group} /run/gitlab + ${pkgs.openssl}/bin/openssl rand -hex 32 > ${cfg.statePath}/gitlab_shell_secret + + ${pkgs.sudo}/bin/sudo -u ${cfg.user} ${cfg.packages.gitlab-shell}/bin/install if ! test -e "${cfg.statePath}/db-created"; then if [ "${cfg.databaseHost}" = "127.0.0.1" ]; then @@ -655,7 +644,7 @@ in { ${pkgs.sudo}/bin/sudo -u ${cfg.user} -H ${gitlab-rake}/bin/gitlab-rake db:schema:load - touch "${cfg.statePath}/db-created" + ${pkgs.sudo}/bin/sudo -u ${cfg.user} touch "${cfg.statePath}/db-created" fi # Always do the db migrations just to be sure the database is up-to-date @@ -664,22 +653,13 @@ in { if ! test -e "${cfg.statePath}/db-seeded"; then ${pkgs.sudo}/bin/sudo -u ${cfg.user} ${gitlab-rake}/bin/gitlab-rake db:seed_fu \ GITLAB_ROOT_PASSWORD='${cfg.initialRootPassword}' GITLAB_ROOT_EMAIL='${cfg.initialRootEmail}' - touch "${cfg.statePath}/db-seeded" + ${pkgs.sudo}/bin/sudo -u ${cfg.user} touch "${cfg.statePath}/db-seeded" fi - # The gitlab:shell:create_hooks task seems broken for fixing links - # so we instead delete all the hooks and create them anew + # We remove potentially broken links to old gitlab-shell versions rm -f ${cfg.statePath}/repositories/**/*.git/hooks - ${pkgs.sudo}/bin/sudo -u ${cfg.user} -H ${gitlab-rake}/bin/gitlab-rake gitlab:shell:create_hooks ${pkgs.sudo}/bin/sudo -u ${cfg.user} -H ${pkgs.git}/bin/git config --global core.autocrlf "input" - - # Change permissions in the last step because some of the - # intermediary scripts like to create directories as root. - chmod -R u+rwX,go-rwx+X ${gitlabEnv.HOME} - chmod -R ug+rwX,o-rwx ${cfg.statePath}/repositories - chmod -R ug-s ${cfg.statePath}/repositories - find ${cfg.statePath}/repositories -type d -print0 | xargs -0 chmod g+s ''; serviceConfig = { diff --git a/nixos/modules/services/misc/jackett.nix b/nixos/modules/services/misc/jackett.nix index a07f20e5c24..f2dc6635df9 100644 --- a/nixos/modules/services/misc/jackett.nix +++ b/nixos/modules/services/misc/jackett.nix @@ -34,6 +34,13 @@ in default = "jackett"; description = "Group under which Jackett runs."; }; + + package = mkOption { + type = types.package; + default = pkgs.jackett; + defaultText = "pkgs.jackett"; + description = "Jackett package to use."; + }; }; }; @@ -51,7 +58,7 @@ in Type = "simple"; User = cfg.user; Group = cfg.group; - ExecStart = "${pkgs.jackett}/bin/Jackett --NoUpdates --DataFolder '${cfg.dataDir}'"; + ExecStart = "${cfg.package}/bin/Jackett --NoUpdates --DataFolder '${cfg.dataDir}'"; Restart = "on-failure"; }; }; diff --git a/nixos/modules/services/misc/lidarr.nix b/nixos/modules/services/misc/lidarr.nix index 4c37bd74f15..40755c16217 100644 --- a/nixos/modules/services/misc/lidarr.nix +++ b/nixos/modules/services/misc/lidarr.nix @@ -68,6 +68,7 @@ in users.users = mkIf (cfg.user == "lidarr") { lidarr = { group = cfg.group; + home = "/var/lib/lidarr"; uid = config.ids.uids.lidarr; }; }; diff --git a/nixos/modules/services/misc/redmine.nix b/nixos/modules/services/misc/redmine.nix index 91ddf2c3edf..24b9e27ac2d 100644 --- a/nixos/modules/services/misc/redmine.nix +++ b/nixos/modules/services/misc/redmine.nix @@ -1,8 +1,10 @@ { config, lib, pkgs, ... }: -with lib; - let + inherit (lib) mkDefault mkEnableOption mkIf mkOption types; + inherit (lib) concatStringsSep literalExample mapAttrsToList; + inherit (lib) optional optionalAttrs optionalString singleton versionAtLeast; + cfg = config.services.redmine; bundle = "${cfg.package}/share/redmine/bin/bundle"; @@ -11,11 +13,11 @@ let production: adapter: ${cfg.database.type} database: ${cfg.database.name} - host: ${cfg.database.host} + host: ${if (cfg.database.type == "postgresql" && cfg.database.socket != null) then cfg.database.socket else cfg.database.host} port: ${toString cfg.database.port} username: ${cfg.database.user} password: #dbpass# - ${optionalString (cfg.database.socket != null) "socket: ${cfg.database.socket}"} + ${optionalString (cfg.database.type == "mysql2" && cfg.database.socket != null) "socket: ${cfg.database.socket}"} ''; configurationYml = pkgs.writeText "configuration.yml" '' @@ -50,16 +52,15 @@ let ''; }); + mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql2"; + pgsqlLocal = cfg.database.createLocally && cfg.database.type == "postgresql"; + in { options = { services.redmine = { - enable = mkOption { - type = types.bool; - default = false; - description = "Enable the Redmine service."; - }; + enable = mkEnableOption "Redmine"; # default to the 4.x series not forcing major version upgrade of those on the 3.x series package = mkOption { @@ -107,7 +108,8 @@ in description = '' Extra configuration in configuration.yml. - See https://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-configuration + See <link xlink:href="https://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-configuration"/> + for details. ''; example = literalExample '' email_delivery: @@ -124,7 +126,8 @@ in description = '' Extra configuration in additional_environment.rb. - See https://svn.redmine.org/redmine/trunk/config/additional_environment.rb.example + See <link xlink:href="https://svn.redmine.org/redmine/trunk/config/additional_environment.rb.example"/> + for details. ''; example = literalExample '' config.logger.level = Logger::DEBUG @@ -169,13 +172,14 @@ in host = mkOption { type = types.str; - default = (if cfg.database.socket != null then "localhost" else "127.0.0.1"); + default = "localhost"; description = "Database host address."; }; port = mkOption { type = types.int; - default = 3306; + default = if cfg.database.type == "postgresql" then 5432 else 3306; + defaultText = "3306"; description = "Database host port."; }; @@ -213,10 +217,20 @@ in socket = mkOption { type = types.nullOr types.path; - default = null; + default = + if mysqlLocal then "/run/mysqld/mysqld.sock" + else if pgsqlLocal then "/run/postgresql" + else null; + defaultText = "/run/mysqld/mysqld.sock"; example = "/run/mysqld/mysqld.sock"; description = "Path to the unix socket file to use for authentication."; }; + + createLocally = mkOption { + type = types.bool; + default = true; + description = "Create the database and database user locally."; + }; }; }; }; @@ -227,12 +241,37 @@ in { assertion = cfg.database.passwordFile != null || cfg.database.password != "" || cfg.database.socket != null; message = "one of services.redmine.database.socket, services.redmine.database.passwordFile, or services.redmine.database.password must be set"; } - { assertion = cfg.database.socket != null -> (cfg.database.type == "mysql2"); - message = "Socket authentication is only available for the mysql2 database type"; + { assertion = cfg.database.createLocally -> cfg.database.user == cfg.user; + message = "services.redmine.database.user must be set to ${cfg.user} if services.redmine.database.createLocally is set true"; + } + { assertion = cfg.database.createLocally -> cfg.database.socket != null; + message = "services.redmine.database.socket must be set if services.redmine.database.createLocally is set to true"; + } + { assertion = cfg.database.createLocally -> cfg.database.host == "localhost"; + message = "services.redmine.database.host must be set to localhost if services.redmine.database.createLocally is set to true"; } ]; - environment.systemPackages = [ cfg.package ]; + services.mysql = mkIf mysqlLocal { + enable = true; + package = mkDefault pkgs.mariadb; + ensureDatabases = [ cfg.database.name ]; + ensureUsers = [ + { name = cfg.database.user; + ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; }; + } + ]; + }; + + services.postgresql = mkIf pgsqlLocal { + enable = true; + ensureDatabases = [ cfg.database.name ]; + ensureUsers = [ + { name = cfg.database.user; + ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; }; + } + ]; + }; # create symlinks for the basic directory layout the redmine package expects systemd.tmpfiles.rules = [ @@ -259,7 +298,7 @@ in ]; systemd.services.redmine = { - after = [ "network.target" (if cfg.database.type == "mysql2" then "mysql.service" else "postgresql.service") ]; + after = [ "network.target" ] ++ optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service"; wantedBy = [ "multi-user.target" ]; environment.RAILS_ENV = "production"; environment.RAILS_CACHE = "${cfg.stateDir}/cache"; diff --git a/nixos/modules/services/misc/tiddlywiki.nix b/nixos/modules/services/misc/tiddlywiki.nix new file mode 100644 index 00000000000..2adc08f6cfe --- /dev/null +++ b/nixos/modules/services/misc/tiddlywiki.nix @@ -0,0 +1,52 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.tiddlywiki; + listenParams = concatStrings (mapAttrsToList (n: v: " '${n}=${toString v}' ") cfg.listenOptions); + exe = "${pkgs.nodePackages.tiddlywiki}/lib/node_modules/.bin/tiddlywiki"; + name = "tiddlywiki"; + dataDir = "/var/lib/" + name; + +in { + + options.services.tiddlywiki = { + + enable = mkEnableOption "TiddlyWiki nodejs server"; + + listenOptions = mkOption { + type = types.attrs; + default = {}; + example = { + credentials = "../credentials.csv"; + readers="(authenticated)"; + port = 3456; + }; + description = '' + Parameters passed to <literal>--listen</literal> command. + Refer to <link xlink:href="https://tiddlywiki.com/#WebServer"/> + for details on supported values. + ''; + }; + }; + + config = mkIf cfg.enable { + systemd = { + services.tiddlywiki = { + description = "TiddlyWiki nodejs server"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "simple"; + Restart = "on-failure"; + DynamicUser = true; + StateDirectory = name; + ExecStartPre = "-${exe} ${dataDir} --init server"; + ExecStart = "${exe} ${dataDir} --listen ${listenParams}"; + }; + }; + }; + }; +} diff --git a/nixos/modules/services/misc/zoneminder.nix b/nixos/modules/services/misc/zoneminder.nix index 8d58c2b37c8..cf56ae89b39 100644 --- a/nixos/modules/services/misc/zoneminder.nix +++ b/nixos/modules/services/misc/zoneminder.nix @@ -262,7 +262,7 @@ in { fastcgi_pass ${fcgi.socketType}:${fcgi.socketAddress}; } - location /cache { + location /cache/ { alias /var/cache/${dirName}; } diff --git a/nixos/modules/services/monitoring/grafana.nix b/nixos/modules/services/monitoring/grafana.nix index 5d3f2e6ac28..c2f6b585d49 100644 --- a/nixos/modules/services/monitoring/grafana.nix +++ b/nixos/modules/services/monitoring/grafana.nix @@ -552,6 +552,8 @@ in { description = "Grafana user"; home = cfg.dataDir; createHome = true; + group = "grafana"; }; + users.groups.grafana = {}; }; } diff --git a/nixos/modules/services/monitoring/loki.nix b/nixos/modules/services/monitoring/loki.nix new file mode 100644 index 00000000000..4d11360d07e --- /dev/null +++ b/nixos/modules/services/monitoring/loki.nix @@ -0,0 +1,112 @@ +{ config, lib, pkgs, ... }: + +let + inherit (lib) escapeShellArgs literalExample mkEnableOption mkIf mkOption types; + + cfg = config.services.loki; + + prettyJSON = conf: + pkgs.runCommand "loki-config.json" { } '' + echo '${builtins.toJSON conf}' | ${pkgs.jq}/bin/jq 'del(._module)' > $out + ''; + +in { + options.services.loki = { + enable = mkEnableOption "loki"; + + user = mkOption { + type = types.str; + default = "loki"; + description = '' + User under which the Loki service runs. + ''; + }; + + group = mkOption { + type = types.str; + default = "loki"; + description = '' + Group under which the Loki service runs. + ''; + }; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/loki"; + description = '' + Specify the directory for Loki. + ''; + }; + + configuration = mkOption { + type = types.attrs; + default = {}; + description = '' + Specify the configuration for Loki in Nix. + ''; + }; + + configFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Specify a configuration file that Loki should use. + ''; + }; + + extraFlags = mkOption { + type = types.listOf types.str; + default = []; + example = literalExample [ "--server.http-listen-port=3101" ]; + description = '' + Specify a list of additional command line flags, + which get escaped and are then passed to Loki. + ''; + }; + }; + + config = mkIf cfg.enable { + assertions = [{ + assertion = ( + (cfg.configuration == {} -> cfg.configFile != null) && + (cfg.configFile != null -> cfg.configuration == {}) + ); + message = '' + Please specify either + 'services.loki.configuration' or + 'services.loki.configFile'. + ''; + }]; + + users.groups.${cfg.group} = { }; + users.users.${cfg.user} = { + description = "Loki Service User"; + group = cfg.group; + home = cfg.dataDir; + createHome = true; + isSystemUser = true; + }; + + systemd.services.loki = { + description = "Loki Service Daemon"; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = let + conf = if cfg.configFile == null + then prettyJSON cfg.configuration + else cfg.configFile; + in + { + ExecStart = "${pkgs.grafana-loki}/bin/loki --config.file=${conf} ${escapeShellArgs cfg.extraFlags}"; + User = cfg.user; + Restart = "always"; + PrivateTmp = true; + ProtectHome = true; + ProtectSystem = "full"; + DecvicePolicy = "closed"; + NoNewPrivileges = true; + WorkingDirectory = cfg.dataDir; + }; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix b/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix index c5b84e574b8..eae7a61297d 100644 --- a/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix +++ b/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix @@ -23,12 +23,24 @@ in { to set the peers up. ''; }; + + singleSubnetPerField = mkOption { + type = types.bool; + default = false; + description = '' + By default, all allowed IPs and subnets are comma-separated in the + <literal>allowed_ips</literal> field. With this option enabled, + a single IP and subnet will be listed in fields like <literal>allowed_ip_0</literal>, + <literal>allowed_ip_1</literal> and so on. + ''; + }; }; serviceOpts = { script = '' ${pkgs.prometheus-wireguard-exporter}/bin/prometheus_wireguard_exporter \ -p ${toString cfg.port} \ ${optionalString cfg.verbose "-v"} \ + ${optionalString cfg.singleSubnetPerField "-s"} \ ${optionalString (cfg.wireguardConfig != null) "-n ${cfg.wireguardConfig}"} ''; diff --git a/nixos/modules/services/monitoring/zabbix-agent.nix b/nixos/modules/services/monitoring/zabbix-agent.nix index 0519e7c2ad6..b1645f86110 100644 --- a/nixos/modules/services/monitoring/zabbix-agent.nix +++ b/nixos/modules/services/monitoring/zabbix-agent.nix @@ -1,73 +1,118 @@ -# Zabbix agent daemon. { config, lib, pkgs, ... }: -with lib; - let - cfg = config.services.zabbixAgent; - zabbix = cfg.package; - - stateDir = "/run/zabbix"; - - logDir = "/var/log/zabbix"; - - pidFile = "${stateDir}/zabbix_agentd.pid"; + inherit (lib) mkDefault mkEnableOption mkIf mkOption; + inherit (lib) attrValues concatMapStringsSep literalExample optionalString types; - configFile = pkgs.writeText "zabbix_agentd.conf" - '' - Server = ${cfg.server} + user = "zabbix-agent"; + group = "zabbix-agent"; - LogFile = ${logDir}/zabbix_agentd - - PidFile = ${pidFile} - - StartAgents = 1 + moduleEnv = pkgs.symlinkJoin { + name = "zabbix-agent-module-env"; + paths = attrValues cfg.modules; + }; - ${config.services.zabbixAgent.extraConfig} - ''; + configFile = pkgs.writeText "zabbix_agent.conf" '' + LogType = console + Server = ${cfg.server} + ListenIP = ${cfg.listen.ip} + ListenPort = ${toString cfg.listen.port} + ${optionalString (cfg.modules != {}) "LoadModulePath = ${moduleEnv}/lib"} + ${concatMapStringsSep "\n" (name: "LoadModule = ${name}") (builtins.attrNames cfg.modules)} + ${cfg.extraConfig} + ''; in { - - ###### interface + # interface options = { services.zabbixAgent = { + enable = mkEnableOption "the Zabbix Agent"; - enable = mkOption { - default = false; + package = mkOption { + type = types.package; + default = pkgs.zabbix.agent; + defaultText = "pkgs.zabbix.agent"; + description = "The Zabbix package to use."; + }; + + extraPackages = mkOption { + type = types.listOf types.package; + default = with pkgs; [ nettools ]; + defaultText = "[ nettools ]"; + example = "[ nettools mysql ]"; description = '' - Whether to run the Zabbix monitoring agent on this machine. - It will send monitoring data to a Zabbix server. + Packages to be added to the Zabbix <envar>PATH</envar>. + Typically used to add executables for scripts, but can be anything. ''; }; - package = mkOption { - type = types.attrs; # Note: pkgs.zabbixXY isn't a derivation, but an attrset of { server = ...; agent = ...; }. - default = pkgs.zabbix; - defaultText = "pkgs.zabbix"; - example = literalExample "pkgs.zabbix34"; - description = '' - The Zabbix package to use. + modules = mkOption { + type = types.attrsOf types.package; + description = "A set of modules to load."; + default = {}; + example = literalExample '' + { + "dummy.so" = pkgs.stdenv.mkDerivation { + name = "zabbix-dummy-module-''${cfg.package.version}"; + src = cfg.package.src; + buildInputs = [ cfg.package ]; + sourceRoot = "zabbix-''${cfg.package.version}/src/modules/dummy"; + installPhase = ''' + mkdir -p $out/lib + cp dummy.so $out/lib/ + '''; + }; + } ''; }; server = mkOption { - default = "127.0.0.1"; + type = types.str; description = '' The IP address or hostname of the Zabbix server to connect to. ''; }; + listen = { + ip = mkOption { + type = types.str; + default = "0.0.0.0"; + description = '' + List of comma delimited IP addresses that the agent should listen on. + ''; + }; + + port = mkOption { + type = types.port; + default = 10050; + description = '' + Agent will listen on this port for connections from the server. + ''; + }; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Open ports in the firewall for the Zabbix Agent. + ''; + }; + + # TODO: for bonus points migrate this to https://github.com/NixOS/rfcs/pull/42 extraConfig = mkOption { default = ""; type = types.lines; description = '' - Configuration that is injected verbatim into the configuration file. + Configuration that is injected verbatim into the configuration file. Refer to + <link xlink:href="https://www.zabbix.com/documentation/current/manual/appendix/config/zabbix_agentd"/> + for details on supported values. ''; }; @@ -75,38 +120,38 @@ in }; - - ###### implementation + # implementation config = mkIf cfg.enable { - users.users = mkIf (!config.services.zabbixServer.enable) (singleton - { name = "zabbix"; - uid = config.ids.uids.zabbix; - description = "Zabbix daemon user"; - }); + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ cfg.listen.port ]; + }; - systemd.services."zabbix-agent" = - { description = "Zabbix Agent"; + users.users.${user} = { + description = "Zabbix Agent daemon user"; + inherit group; + }; - wantedBy = [ "multi-user.target" ]; + users.groups.${group} = { }; - path = [ pkgs.nettools ]; + systemd.services."zabbix-agent" = { + description = "Zabbix Agent"; - preStart = - '' - mkdir -m 0755 -p ${stateDir} ${logDir} - chown zabbix ${stateDir} ${logDir} - ''; + wantedBy = [ "multi-user.target" ]; - serviceConfig.ExecStart = "@${zabbix.agent}/sbin/zabbix_agentd zabbix_agentd --config ${configFile}"; - serviceConfig.Type = "forking"; - serviceConfig.RemainAfterExit = true; - serviceConfig.Restart = "always"; - serviceConfig.RestartSec = 2; - }; + path = [ "/run/wrappers" ] ++ cfg.extraPackages; + + serviceConfig = { + ExecStart = "@${cfg.package}/sbin/zabbix_agentd zabbix_agentd -f --config ${configFile}"; + Restart = "always"; + RestartSec = 2; - environment.systemPackages = [ zabbix.agent ]; + User = user; + Group = group; + PrivateTmp = true; + }; + }; }; diff --git a/nixos/modules/services/monitoring/zabbix-proxy.nix b/nixos/modules/services/monitoring/zabbix-proxy.nix new file mode 100644 index 00000000000..c1a45fba4af --- /dev/null +++ b/nixos/modules/services/monitoring/zabbix-proxy.nix @@ -0,0 +1,290 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.zabbixProxy; + pgsql = config.services.postgresql; + mysql = config.services.mysql; + + inherit (lib) mkDefault mkEnableOption mkIf mkOption; + inherit (lib) attrValues concatMapStringsSep literalExample optional optionalAttrs optionalString types; + + user = "zabbix"; + group = "zabbix"; + runtimeDir = "/run/zabbix"; + stateDir = "/var/lib/zabbix"; + passwordFile = "${runtimeDir}/zabbix-dbpassword.conf"; + + moduleEnv = pkgs.symlinkJoin { + name = "zabbix-proxy-module-env"; + paths = attrValues cfg.modules; + }; + + configFile = pkgs.writeText "zabbix_proxy.conf" '' + LogType = console + ListenIP = ${cfg.listen.ip} + ListenPort = ${toString cfg.listen.port} + # TODO: set to cfg.database.socket if database type is pgsql? + DBHost = ${optionalString (cfg.database.createLocally != true) cfg.database.host} + ${optionalString (cfg.database.createLocally != true) "DBPort = ${cfg.database.port}"} + DBName = ${cfg.database.name} + DBUser = ${cfg.database.user} + ${optionalString (cfg.database.passwordFile != null) "Include ${passwordFile}"} + ${optionalString (mysqlLocal && cfg.database.socket != null) "DBSocket = ${cfg.database.socket}"} + SocketDir = ${runtimeDir} + FpingLocation = /run/wrappers/bin/fping + ${optionalString (cfg.modules != {}) "LoadModulePath = ${moduleEnv}/lib"} + ${concatMapStringsSep "\n" (name: "LoadModule = ${name}") (builtins.attrNames cfg.modules)} + ${cfg.extraConfig} + ''; + + mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql"; + pgsqlLocal = cfg.database.createLocally && cfg.database.type == "pgsql"; + +in + +{ + # interface + + options = { + + services.zabbixProxy = { + enable = mkEnableOption "the Zabbix Proxy"; + + package = mkOption { + type = types.package; + default = + if cfg.database.type == "mysql" then pkgs.zabbix.proxy-mysql + else if cfg.database.type == "pgsql" then pkgs.zabbix.proxy-pgsql + else pkgs.zabbix.proxy-sqlite; + defaultText = "pkgs.zabbix.proxy-pgsql"; + description = "The Zabbix package to use."; + }; + + extraPackages = mkOption { + type = types.listOf types.package; + default = with pkgs; [ nettools nmap traceroute ]; + defaultText = "[ nettools nmap traceroute ]"; + description = '' + Packages to be added to the Zabbix <envar>PATH</envar>. + Typically used to add executables for scripts, but can be anything. + ''; + }; + + modules = mkOption { + type = types.attrsOf types.package; + description = "A set of modules to load."; + default = {}; + example = literalExample '' + { + "dummy.so" = pkgs.stdenv.mkDerivation { + name = "zabbix-dummy-module-''${cfg.package.version}"; + src = cfg.package.src; + buildInputs = [ cfg.package ]; + sourceRoot = "zabbix-''${cfg.package.version}/src/modules/dummy"; + installPhase = ''' + mkdir -p $out/lib + cp dummy.so $out/lib/ + '''; + }; + } + ''; + }; + + database = { + type = mkOption { + type = types.enum [ "mysql" "pgsql" "sqlite" ]; + example = "mysql"; + default = "pgsql"; + description = "Database engine to use."; + }; + + host = mkOption { + type = types.str; + default = "localhost"; + description = "Database host address."; + }; + + port = mkOption { + type = types.int; + default = if cfg.database.type == "mysql" then mysql.port else pgsql.port; + description = "Database host port."; + }; + + name = mkOption { + type = types.str; + default = "zabbix"; + description = "Database name."; + }; + + user = mkOption { + type = types.str; + default = "zabbix"; + description = "Database user."; + }; + + passwordFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/run/keys/zabbix-dbpassword"; + description = '' + A file containing the password corresponding to + <option>database.user</option>. + ''; + }; + + socket = mkOption { + type = types.nullOr types.path; + default = null; + example = "/run/postgresql"; + description = "Path to the unix socket file to use for authentication."; + }; + + createLocally = mkOption { + type = types.bool; + default = true; + description = "Whether to create a local database automatically."; + }; + }; + + listen = { + ip = mkOption { + type = types.str; + default = "0.0.0.0"; + description = '' + List of comma delimited IP addresses that the trapper should listen on. + Trapper will listen on all network interfaces if this parameter is missing. + ''; + }; + + port = mkOption { + type = types.port; + default = 10051; + description = '' + Listen port for trapper. + ''; + }; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Open ports in the firewall for the Zabbix Proxy. + ''; + }; + + # TODO: for bonus points migrate this to https://github.com/NixOS/rfcs/pull/42 + extraConfig = mkOption { + default = ""; + type = types.lines; + description = '' + Configuration that is injected verbatim into the configuration file. Refer to + <link xlink:href="https://www.zabbix.com/documentation/current/manual/appendix/config/zabbix_proxy"/> + for details on supported values. + ''; + }; + + }; + + }; + + # implementation + + config = mkIf cfg.enable { + + assertions = [ + { assertion = !config.services.zabbixServer.enable; + message = "Please choose one of services.zabbixServer or services.zabbixProxy."; + } + { assertion = cfg.database.createLocally -> cfg.database.user == user; + message = "services.zabbixProxy.database.user must be set to ${user} if services.zabbixProxy.database.createLocally is set true"; + } + { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null; + message = "a password cannot be specified if services.zabbixProxy.database.createLocally is set to true"; + } + ]; + + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ cfg.listen.port ]; + }; + + services.mysql = optionalAttrs mysqlLocal { + enable = true; + package = mkDefault pkgs.mariadb; + ensureDatabases = [ cfg.database.name ]; + ensureUsers = [ + { name = cfg.database.user; + ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; }; + } + ]; + }; + + services.postgresql = optionalAttrs pgsqlLocal { + enable = true; + ensureDatabases = [ cfg.database.name ]; + ensureUsers = [ + { name = cfg.database.user; + ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; }; + } + ]; + }; + + users.users.${user} = { + description = "Zabbix daemon user"; + uid = config.ids.uids.zabbix; + inherit group; + }; + + users.groups.${group} = { + gid = config.ids.gids.zabbix; + }; + + security.wrappers = { + fping.source = "${pkgs.fping}/bin/fping"; + }; + + systemd.services."zabbix-proxy" = { + description = "Zabbix Proxy"; + + wantedBy = [ "multi-user.target" ]; + after = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service"; + + path = [ "/run/wrappers" ] ++ cfg.extraPackages; + preStart = optionalString pgsqlLocal '' + if ! test -e "${stateDir}/db-created"; then + cat ${cfg.package}/share/zabbix/database/postgresql/schema.sql | ${pgsql.package}/bin/psql ${cfg.database.name} + cat ${cfg.package}/share/zabbix/database/postgresql/images.sql | ${pgsql.package}/bin/psql ${cfg.database.name} + cat ${cfg.package}/share/zabbix/database/postgresql/data.sql | ${pgsql.package}/bin/psql ${cfg.database.name} + touch "${stateDir}/db-created" + fi + '' + optionalString mysqlLocal '' + if ! test -e "${stateDir}/db-created"; then + cat ${cfg.package}/share/zabbix/database/mysql/schema.sql | ${mysql.package}/bin/mysql ${cfg.database.name} + cat ${cfg.package}/share/zabbix/database/mysql/images.sql | ${mysql.package}/bin/mysql ${cfg.database.name} + cat ${cfg.package}/share/zabbix/database/mysql/data.sql | ${mysql.package}/bin/mysql ${cfg.database.name} + touch "${stateDir}/db-created" + fi + '' + optionalString (cfg.database.passwordFile != null) '' + # create a copy of the supplied password file in a format zabbix can consume + touch ${passwordFile} + chmod 0600 ${passwordFile} + echo -n "DBPassword = " > ${passwordFile} + cat ${cfg.database.passwordFile} >> ${passwordFile} + ''; + + serviceConfig = { + ExecStart = "@${cfg.package}/sbin/zabbix_proxy zabbix_proxy -f --config ${configFile}"; + Restart = "always"; + RestartSec = 2; + + User = user; + Group = group; + RuntimeDirectory = "zabbix"; + StateDirectory = "zabbix"; + PrivateTmp = true; + }; + }; + + }; + +} diff --git a/nixos/modules/services/monitoring/zabbix-server.nix b/nixos/modules/services/monitoring/zabbix-server.nix index fdeab6af441..11311b466c3 100644 --- a/nixos/modules/services/monitoring/zabbix-server.nix +++ b/nixos/modules/services/monitoring/zabbix-server.nix @@ -1,125 +1,292 @@ -# Zabbix server daemon. { config, lib, pkgs, ... }: -with lib; - let - cfg = config.services.zabbixServer; + pgsql = config.services.postgresql; + mysql = config.services.mysql; - stateDir = "/run/zabbix"; + inherit (lib) mkDefault mkEnableOption mkIf mkOption; + inherit (lib) attrValues concatMapStringsSep literalExample optional optionalAttrs optionalString types; - logDir = "/var/log/zabbix"; + user = "zabbix"; + group = "zabbix"; + runtimeDir = "/run/zabbix"; + stateDir = "/var/lib/zabbix"; + passwordFile = "${runtimeDir}/zabbix-dbpassword.conf"; - libDir = "/var/lib/zabbix"; + moduleEnv = pkgs.symlinkJoin { + name = "zabbix-server-module-env"; + paths = attrValues cfg.modules; + }; - pidFile = "${stateDir}/zabbix_server.pid"; + configFile = pkgs.writeText "zabbix_server.conf" '' + LogType = console + ListenIP = ${cfg.listen.ip} + ListenPort = ${toString cfg.listen.port} + # TODO: set to cfg.database.socket if database type is pgsql? + DBHost = ${optionalString (cfg.database.createLocally != true) cfg.database.host} + ${optionalString (cfg.database.createLocally != true) "DBPort = ${cfg.database.port}"} + DBName = ${cfg.database.name} + DBUser = ${cfg.database.user} + ${optionalString (cfg.database.passwordFile != null) "Include ${passwordFile}"} + ${optionalString (mysqlLocal && cfg.database.socket != null) "DBSocket = ${cfg.database.socket}"} + SocketDir = ${runtimeDir} + FpingLocation = /run/wrappers/bin/fping + ${optionalString (cfg.modules != {}) "LoadModulePath = ${moduleEnv}/lib"} + ${concatMapStringsSep "\n" (name: "LoadModule = ${name}") (builtins.attrNames cfg.modules)} + ${cfg.extraConfig} + ''; + + mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql"; + pgsqlLocal = cfg.database.createLocally && cfg.database.type == "pgsql"; - configFile = pkgs.writeText "zabbix_server.conf" - '' - LogFile = ${logDir}/zabbix_server +in - PidFile = ${pidFile} +{ + # interface - ${optionalString (cfg.dbServer != "localhost") '' - DBHost = ${cfg.dbServer} - ''} + options = { - DBName = zabbix + services.zabbixServer = { + enable = mkEnableOption "the Zabbix Server"; - DBUser = zabbix + package = mkOption { + type = types.package; + default = if cfg.database.type == "mysql" then pkgs.zabbix.server-mysql else pkgs.zabbix.server-pgsql; + defaultText = "pkgs.zabbix.server-pgsql"; + description = "The Zabbix package to use."; + }; - ${optionalString (cfg.dbPassword != "") '' - DBPassword = ${cfg.dbPassword} - ''} + extraPackages = mkOption { + type = types.listOf types.package; + default = with pkgs; [ nettools nmap traceroute ]; + defaultText = "[ nettools nmap traceroute ]"; + description = '' + Packages to be added to the Zabbix <envar>PATH</envar>. + Typically used to add executables for scripts, but can be anything. + ''; + }; - ${config.services.zabbixServer.extraConfig} - ''; + modules = mkOption { + type = types.attrsOf types.package; + description = "A set of modules to load."; + default = {}; + example = literalExample '' + { + "dummy.so" = pkgs.stdenv.mkDerivation { + name = "zabbix-dummy-module-''${cfg.package.version}"; + src = cfg.package.src; + buildInputs = [ cfg.package ]; + sourceRoot = "zabbix-''${cfg.package.version}/src/modules/dummy"; + installPhase = ''' + mkdir -p $out/lib + cp dummy.so $out/lib/ + '''; + }; + } + ''; + }; - useLocalPostgres = cfg.dbServer == "localhost" || cfg.dbServer == ""; + database = { + type = mkOption { + type = types.enum [ "mysql" "pgsql" ]; + example = "mysql"; + default = "pgsql"; + description = "Database engine to use."; + }; + + host = mkOption { + type = types.str; + default = "localhost"; + description = "Database host address."; + }; + + port = mkOption { + type = types.int; + default = if cfg.database.type == "mysql" then mysql.port else pgsql.port; + description = "Database host port."; + }; + + name = mkOption { + type = types.str; + default = "zabbix"; + description = "Database name."; + }; + + user = mkOption { + type = types.str; + default = "zabbix"; + description = "Database user."; + }; + + passwordFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/run/keys/zabbix-dbpassword"; + description = '' + A file containing the password corresponding to + <option>database.user</option>. + ''; + }; + + socket = mkOption { + type = types.nullOr types.path; + default = null; + example = "/run/postgresql"; + description = "Path to the unix socket file to use for authentication."; + }; + + createLocally = mkOption { + type = types.bool; + default = true; + description = "Whether to create a local database automatically."; + }; + }; -in + listen = { + ip = mkOption { + type = types.str; + default = "0.0.0.0"; + description = '' + List of comma delimited IP addresses that the trapper should listen on. + Trapper will listen on all network interfaces if this parameter is missing. + ''; + }; -{ + port = mkOption { + type = types.port; + default = 10051; + description = '' + Listen port for trapper. + ''; + }; + }; - ###### interface + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Open ports in the firewall for the Zabbix Server. + ''; + }; - options = { + # TODO: for bonus points migrate this to https://github.com/NixOS/rfcs/pull/42 + extraConfig = mkOption { + default = ""; + type = types.lines; + description = '' + Configuration that is injected verbatim into the configuration file. Refer to + <link xlink:href="https://www.zabbix.com/documentation/current/manual/appendix/config/zabbix_server"/> + for details on supported values. + ''; + }; - services.zabbixServer.enable = mkOption { - default = false; - type = types.bool; - description = '' - Whether to run the Zabbix server on this machine. - ''; }; - services.zabbixServer.dbServer = mkOption { - default = "localhost"; - type = types.str; - description = '' - Hostname or IP address of the database server. - Use an empty string ("") to use peer authentication. - ''; - }; + }; - services.zabbixServer.dbPassword = mkOption { - default = ""; - type = types.str; - description = "Password used to connect to the database server."; - }; + # implementation - services.zabbixServer.extraConfig = mkOption { - default = ""; - type = types.lines; - description = '' - Configuration that is injected verbatim into the configuration file. - ''; + config = mkIf cfg.enable { + + assertions = [ + { assertion = cfg.database.createLocally -> cfg.database.user == user; + message = "services.zabbixServer.database.user must be set to ${user} if services.zabbixServer.database.createLocally is set true"; + } + { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null; + message = "a password cannot be specified if services.zabbixServer.database.createLocally is set to true"; + } + ]; + + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ cfg.listen.port ]; }; - }; + services.mysql = optionalAttrs mysqlLocal { + enable = true; + package = mkDefault pkgs.mariadb; + ensureDatabases = [ cfg.database.name ]; + ensureUsers = [ + { name = cfg.database.user; + ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; }; + } + ]; + }; - ###### implementation + services.postgresql = optionalAttrs pgsqlLocal { + enable = true; + ensureDatabases = [ cfg.database.name ]; + ensureUsers = [ + { name = cfg.database.user; + ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; }; + } + ]; + }; - config = mkIf cfg.enable { + users.users.${user} = { + description = "Zabbix daemon user"; + uid = config.ids.uids.zabbix; + inherit group; + }; - services.postgresql.enable = useLocalPostgres; + users.groups.${group} = { + gid = config.ids.gids.zabbix; + }; - users.users = singleton - { name = "zabbix"; - uid = config.ids.uids.zabbix; - description = "Zabbix daemon user"; - }; + security.wrappers = { + fping.source = "${pkgs.fping}/bin/fping"; + }; - systemd.services."zabbix-server" = - { description = "Zabbix Server"; - - wantedBy = [ "multi-user.target" ]; - after = optional useLocalPostgres "postgresql.service"; - - preStart = - '' - mkdir -m 0755 -p ${stateDir} ${logDir} ${libDir} - chown zabbix ${stateDir} ${logDir} ${libDir} - - if ! test -e "${libDir}/db-created"; then - ${pkgs.su}/bin/su -s "$SHELL" ${config.services.postgresql.superUser} -c '${pkgs.postgresql}/bin/createuser --no-superuser --no-createdb --no-createrole zabbix' || true - ${pkgs.su}/bin/su -s "$SHELL" ${config.services.postgresql.superUser} -c '${pkgs.postgresql}/bin/createdb --owner zabbix zabbix' || true - cat ${pkgs.zabbix.server}/share/zabbix/db/schema/postgresql.sql | ${pkgs.su}/bin/su -s "$SHELL" zabbix -c '${pkgs.postgresql}/bin/psql zabbix' - cat ${pkgs.zabbix.server}/share/zabbix/db/data/images_pgsql.sql | ${pkgs.su}/bin/su -s "$SHELL" zabbix -c '${pkgs.postgresql}/bin/psql zabbix' - cat ${pkgs.zabbix.server}/share/zabbix/db/data/data.sql | ${pkgs.su}/bin/su -s "$SHELL" zabbix -c '${pkgs.postgresql}/bin/psql zabbix' - touch "${libDir}/db-created" - fi - ''; + systemd.services."zabbix-server" = { + description = "Zabbix Server"; + + wantedBy = [ "multi-user.target" ]; + after = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service"; + + path = [ "/run/wrappers" ] ++ cfg.extraPackages; + preStart = '' + # pre 19.09 compatibility + if test -e "${runtimeDir}/db-created"; then + mv "${runtimeDir}/db-created" "${stateDir}/" + fi + '' + optionalString pgsqlLocal '' + if ! test -e "${stateDir}/db-created"; then + cat ${cfg.package}/share/zabbix/database/postgresql/schema.sql | ${pgsql.package}/bin/psql ${cfg.database.name} + cat ${cfg.package}/share/zabbix/database/postgresql/images.sql | ${pgsql.package}/bin/psql ${cfg.database.name} + cat ${cfg.package}/share/zabbix/database/postgresql/data.sql | ${pgsql.package}/bin/psql ${cfg.database.name} + touch "${stateDir}/db-created" + fi + '' + optionalString mysqlLocal '' + if ! test -e "${stateDir}/db-created"; then + cat ${cfg.package}/share/zabbix/database/mysql/schema.sql | ${mysql.package}/bin/mysql ${cfg.database.name} + cat ${cfg.package}/share/zabbix/database/mysql/images.sql | ${mysql.package}/bin/mysql ${cfg.database.name} + cat ${cfg.package}/share/zabbix/database/mysql/data.sql | ${mysql.package}/bin/mysql ${cfg.database.name} + touch "${stateDir}/db-created" + fi + '' + optionalString (cfg.database.passwordFile != null) '' + # create a copy of the supplied password file in a format zabbix can consume + touch ${passwordFile} + chmod 0600 ${passwordFile} + echo -n "DBPassword = " > ${passwordFile} + cat ${cfg.database.passwordFile} >> ${passwordFile} + ''; - path = [ pkgs.nettools ]; + serviceConfig = { + ExecStart = "@${cfg.package}/sbin/zabbix_server zabbix_server -f --config ${configFile}"; + Restart = "always"; + RestartSec = 2; - serviceConfig.ExecStart = "@${pkgs.zabbix.server}/sbin/zabbix_server zabbix_server --config ${configFile}"; - serviceConfig.Type = "forking"; - serviceConfig.Restart = "always"; - serviceConfig.RestartSec = 2; - serviceConfig.PIDFile = pidFile; + User = user; + Group = group; + RuntimeDirectory = "zabbix"; + StateDirectory = "zabbix"; + PrivateTmp = true; }; + }; + + systemd.services.httpd.after = + optional (config.services.zabbixWeb.enable && mysqlLocal) "mysql.service" ++ + optional (config.services.zabbixWeb.enable && pgsqlLocal) "postgresql.service"; }; diff --git a/nixos/modules/services/networking/bind.nix b/nixos/modules/services/networking/bind.nix index 7f89cff2232..2097b9a3163 100644 --- a/nixos/modules/services/networking/bind.nix +++ b/nixos/modules/services/networking/bind.nix @@ -168,7 +168,9 @@ in ###### implementation - config = mkIf config.services.bind.enable { + config = mkIf cfg.enable { + + networking.resolvconf.useLocalResolver = mkDefault true; users.users = singleton { name = bindUser; diff --git a/nixos/modules/services/networking/dhcpcd.nix b/nixos/modules/services/networking/dhcpcd.nix index c217ccaa405..7b278603455 100644 --- a/nixos/modules/services/networking/dhcpcd.nix +++ b/nixos/modules/services/networking/dhcpcd.nix @@ -162,7 +162,7 @@ in wantedBy = [ "multi-user.target" ] ++ optional (!hasDefaultGatewaySet) "network-online.target"; wants = [ "network.target" "systemd-udev-settle.service" ]; - before = [ "network.target" ]; + before = [ "network-online.target" ]; after = [ "systemd-udev-settle.service" ]; # Stopping dhcpcd during a reconfiguration is undesirable diff --git a/nixos/modules/services/networking/dnsmasq.nix b/nixos/modules/services/networking/dnsmasq.nix index 24d16046c63..714a5903bff 100644 --- a/nixos/modules/services/networking/dnsmasq.nix +++ b/nixos/modules/services/networking/dnsmasq.nix @@ -79,7 +79,7 @@ in ###### implementation - config = mkIf config.services.dnsmasq.enable { + config = mkIf cfg.enable { networking.nameservers = optional cfg.resolveLocalQueries "127.0.0.1"; @@ -92,6 +92,15 @@ in description = "Dnsmasq daemon user"; }; + networking.resolvconf = mkIf cfg.resolveLocalQueries { + useLocalResolver = mkDefault true; + + extraConfig = '' + dnsmasq_conf=/etc/dnsmasq-conf.conf + dnsmasq_resolv=/etc/dnsmasq-resolv.conf + ''; + }; + systemd.services.dnsmasq = { description = "Dnsmasq Daemon"; after = [ "network.target" "systemd-resolved.service" ]; diff --git a/nixos/modules/services/networking/kresd.nix b/nixos/modules/services/networking/kresd.nix index ca34ff9df4e..fc516c01230 100644 --- a/nixos/modules/services/networking/kresd.nix +++ b/nixos/modules/services/networking/kresd.nix @@ -80,8 +80,11 @@ in # Syntax depends on being IPv6 or IPv4. (iface: if elem ":" (stringToCharacters iface) then "[${iface}]:53" else "${iface}:53") cfg.interfaces; - socketConfig.ListenDatagram = listenStreams; - socketConfig.FreeBind = true; + socketConfig = { + ListenDatagram = listenStreams; + FreeBind = true; + FileDescriptorName = "dns"; + }; }; systemd.sockets.kresd-tls = mkIf (cfg.listenTLS != []) rec { diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix index f1f8c9722e0..ab6065b2008 100644 --- a/nixos/modules/services/networking/networkmanager.nix +++ b/nixos/modules/services/networking/networkmanager.nix @@ -1,6 +1,5 @@ { config, lib, pkgs, ... }: -with pkgs; with lib; let @@ -12,12 +11,13 @@ let # /var/lib/misc is for dnsmasq.leases. stateDirs = "/var/lib/NetworkManager /var/lib/dhclient /var/lib/misc"; - configFile = writeText "NetworkManager.conf" '' + configFile = pkgs.writeText "NetworkManager.conf" '' [main] plugins=keyfile dhcp=${cfg.dhcp} dns=${cfg.dns} - rc-manager=${cfg.rc-manager} + # If resolvconf is disabled that means that resolv.conf is managed by some other module. + rc-manager=${if config.networking.resolvconf.enable then "resolvconf" else "unmanaged"} [keyfile] ${optionalString (cfg.unmanaged != []) @@ -65,19 +65,19 @@ let }); ''; - ns = xs: writeText "nameservers" ( + ns = xs: pkgs.writeText "nameservers" ( concatStrings (map (s: "nameserver ${s}\n") xs) ); - overrideNameserversScript = writeScript "02overridedns" '' + overrideNameserversScript = pkgs.writeScript "02overridedns" '' #!/bin/sh - tmp=`${coreutils}/bin/mktemp` - ${gnused}/bin/sed '/nameserver /d' /etc/resolv.conf > $tmp - ${gnugrep}/bin/grep 'nameserver ' /etc/resolv.conf | \ - ${gnugrep}/bin/grep -vf ${ns (cfg.appendNameservers ++ cfg.insertNameservers)} > $tmp.ns - ${optionalString (cfg.appendNameservers != []) "${coreutils}/bin/cat $tmp $tmp.ns ${ns cfg.appendNameservers} > /etc/resolv.conf"} - ${optionalString (cfg.insertNameservers != []) "${coreutils}/bin/cat $tmp ${ns cfg.insertNameservers} $tmp.ns > /etc/resolv.conf"} - ${coreutils}/bin/rm -f $tmp $tmp.ns + PATH=${with pkgs; makeBinPath [ gnused gnugrep coreutils ]} + tmp=$(mktemp) + sed '/nameserver /d' /etc/resolv.conf > $tmp + grep 'nameserver ' /etc/resolv.conf | \ + grep -vf ${ns (cfg.appendNameservers ++ cfg.insertNameservers)} > $tmp.ns + cat $tmp ${ns cfg.insertNameservers} $tmp.ns ${ns cfg.appendNameservers} > /etc/resolv.conf + rm -f $tmp $tmp.ns ''; dispatcherTypesSubdirMap = { @@ -176,7 +176,8 @@ in { # Ugly hack for using the correct gnome3 packageSet basePackages = mkOption { type = types.attrsOf types.package; - default = { inherit networkmanager modemmanager wpa_supplicant + default = { inherit (pkgs) + networkmanager modemmanager wpa_supplicant networkmanager-openvpn networkmanager-vpnc networkmanager-openconnect networkmanager-fortisslvpn networkmanager-l2tp networkmanager-iodine; }; @@ -268,25 +269,6 @@ in { ''; }; - rc-manager = mkOption { - type = types.enum [ "symlink" "file" "resolvconf" "netconfig" "unmanaged" "none" ]; - default = "resolvconf"; - description = '' - Set the <literal>resolv.conf</literal> management mode. - </para> - <para> - A description of these modes can be found in the main section of - <link xlink:href="https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html"> - https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html - </link> - or in - <citerefentry> - <refentrytitle>NetworkManager.conf</refentrytitle> - <manvolnum>5</manvolnum> - </citerefentry>. - ''; - }; - dispatcherScripts = mkOption { type = types.listOf (types.submodule { options = { @@ -425,13 +407,10 @@ in { { source = "${networkmanager-l2tp}/lib/NetworkManager/VPN/nm-l2tp-service.name"; target = "NetworkManager/VPN/nm-l2tp-service.name"; } - { source = "${networkmanager_strongswan}/lib/NetworkManager/VPN/nm-strongswan-service.name"; - target = "NetworkManager/VPN/nm-strongswan-service.name"; - } { source = "${networkmanager-iodine}/lib/NetworkManager/VPN/nm-iodine-service.name"; target = "NetworkManager/VPN/nm-iodine-service.name"; } - ] ++ optional (cfg.appendNameservers == [] || cfg.insertNameservers == []) + ] ++ optional (cfg.appendNameservers != [] || cfg.insertNameservers != []) { source = overrideNameserversScript; target = "NetworkManager/dispatcher.d/02overridedns"; } @@ -440,11 +419,15 @@ in { target = "NetworkManager/dispatcher.d/${dispatcherTypesSubdirMap.${s.type}}03userscript${lib.fixedWidthNumber 4 i}"; mode = "0544"; }) cfg.dispatcherScripts - ++ optional (dynamicHostsEnabled) + ++ optional dynamicHostsEnabled { target = "NetworkManager/dnsmasq.d/dyndns.conf"; text = concatMapStrings (n: '' hostsdir=/run/NetworkManager/hostsdirs/${n} '') (attrNames cfg.dynamicHosts.hostsDirs); + } + ++ optional cfg.enableStrongSwan + { source = "${pkgs.networkmanager_strongswan}/lib/NetworkManager/VPN/nm-strongswan-service.name"; + target = "NetworkManager/VPN/nm-strongswan-service.name"; }; environment.systemPackages = cfg.packages; @@ -512,7 +495,7 @@ in { networking = { useDHCP = false; # use mkDefault to trigger the assertion about the conflict above - wireless.enable = lib.mkDefault false; + wireless.enable = mkDefault false; }; security.polkit.extraConfig = polkitConf; diff --git a/nixos/modules/services/networking/rdnssd.nix b/nixos/modules/services/networking/rdnssd.nix index 887772f6e5f..bccab805bee 100644 --- a/nixos/modules/services/networking/rdnssd.nix +++ b/nixos/modules/services/networking/rdnssd.nix @@ -35,6 +35,11 @@ in config = mkIf config.services.rdnssd.enable { + assertions = [{ + assertion = config.networking.resolvconf.enable; + message = "rdnssd needs resolvconf to work (probably something sets up a static resolv.conf)"; + }]; + systemd.services.rdnssd = { description = "RDNSS daemon"; after = [ "network.target" ]; diff --git a/nixos/modules/services/networking/unbound.nix b/nixos/modules/services/networking/unbound.nix index 1a35979ad44..3cf82e8839b 100644 --- a/nixos/modules/services/networking/unbound.nix +++ b/nixos/modules/services/networking/unbound.nix @@ -101,6 +101,8 @@ in isSystemUser = true; }; + networking.resolvconf.useLocalResolver = mkDefault true; + systemd.services.unbound = { description = "Unbound recursive Domain Name Server"; after = [ "network.target" ]; diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix index 6f4852c3ba1..abdc0cd78b4 100644 --- a/nixos/modules/services/security/tor.nix +++ b/nixos/modules/services/security/tor.nix @@ -81,7 +81,7 @@ let ${optionalString (elem cfg.relay.role ["bridge" "private-bridge"]) '' BridgeRelay 1 - ServerTransportPlugin ${concatStringsSep "," cfg.relay.bridgeTransports} exec ${obfs4}/bin/obfs4proxy managed + ServerTransportPlugin ${concatStringsSep "," cfg.relay.bridgeTransports} exec ${pkgs.obfs4}/bin/obfs4proxy managed ExtORPort auto ${optionalString (cfg.relay.role == "private-bridge") '' ExtraInfoStatistics 0 diff --git a/nixos/modules/services/torrent/deluge.nix b/nixos/modules/services/torrent/deluge.nix index 01a5890a784..48ec4d692e2 100644 --- a/nixos/modules/services/torrent/deluge.nix +++ b/nixos/modules/services/torrent/deluge.nix @@ -118,36 +118,74 @@ in { more informations. ''; }; + + user = mkOption { + type = types.str; + default = "deluge"; + description = '' + User account under which deluge runs. + ''; + }; + + group = mkOption { + type = types.str; + default = "deluge"; + description = '' + Group under which deluge runs. + ''; + }; + + extraPackages = mkOption { + type = types.listOf types.package; + default = []; + description = '' + Extra packages available at runtime to enable Deluge's plugins. For example, + extraction utilities are required for the built-in "Extractor" plugin. + This always contains unzip, gnutar, xz, p7zip and bzip2. + ''; + }; }; deluge.web = { enable = mkEnableOption "Deluge Web daemon"; + port = mkOption { - type = types.port; + type = types.port; default = 8112; description = '' Deluge web UI port. ''; }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Open ports in the firewall for deluge web daemon + ''; + }; }; }; }; config = mkIf cfg.enable { - systemd.tmpfiles.rules = [ "d '${configDir}' 0770 deluge deluge" ] + # Provide a default set of `extraPackages`. + services.deluge.extraPackages = with pkgs; [ unzip gnutar xz p7zip bzip2 ]; + + systemd.tmpfiles.rules = [ "d '${configDir}' 0770 ${cfg.user} ${cfg.group}" ] ++ optional (cfg.config ? "download_location") - "d '${cfg.config.download_location}' 0770 deluge deluge" + "d '${cfg.config.download_location}' 0770 ${cfg.user} ${cfg.group}" ++ optional (cfg.config ? "torrentfiles_location") - "d '${cfg.config.torrentfiles_location}' 0770 deluge deluge" + "d '${cfg.config.torrentfiles_location}' 0770 ${cfg.user} ${cfg.group}" ++ optional (cfg.config ? "move_completed_path") - "d '${cfg.config.move_completed_path}' 0770 deluge deluge"; + "d '${cfg.config.move_completed_path}' 0770 ${cfg.user} ${cfg.group}"; systemd.services.deluged = { after = [ "network.target" ]; description = "Deluge BitTorrent Daemon"; wantedBy = [ "multi-user.target" ]; - path = [ pkgs.deluge ]; + path = [ pkgs.deluge ] ++ cfg.extraPackages; serviceConfig = { ExecStart = '' ${pkgs.deluge}/bin/deluged \ @@ -157,8 +195,8 @@ in { # To prevent "Quit & shutdown daemon" from working; we want systemd to # manage it! Restart = "on-success"; - User = "deluge"; - Group = "deluge"; + User = cfg.user; + Group = cfg.group; UMask = "0002"; LimitNOFILE = cfg.openFilesLimit; }; @@ -177,26 +215,37 @@ in { --config ${configDir} \ --port ${toString cfg.web.port} ''; - User = "deluge"; - Group = "deluge"; + User = cfg.user; + Group = cfg.group; }; }; - networking.firewall = mkIf (cfg.declarative && cfg.openFirewall && !(cfg.config.random_port or true)) { - allowedTCPPortRanges = singleton (listToRange (cfg.config.listen_ports or listenPortsDefault)); - allowedUDPPortRanges = singleton (listToRange (cfg.config.listen_ports or listenPortsDefault)); - }; + networking.firewall = mkMerge [ + (mkIf (cfg.declarative && cfg.openFirewall && !(cfg.config.random_port or true)) { + allowedTCPPortRanges = singleton (listToRange (cfg.config.listen_ports or listenPortsDefault)); + allowedUDPPortRanges = singleton (listToRange (cfg.config.listen_ports or listenPortsDefault)); + }) + (mkIf (cfg.web.openFirewall) { + allowedTCPPorts = [ cfg.web.port ]; + }) + ]; environment.systemPackages = [ pkgs.deluge ]; - users.users.deluge = { - group = "deluge"; - uid = config.ids.uids.deluge; - home = cfg.dataDir; - createHome = true; - description = "Deluge Daemon user"; + users.users = mkIf (cfg.user == "deluge") { + deluge = { + group = cfg.group; + uid = config.ids.uids.deluge; + home = cfg.dataDir; + createHome = true; + description = "Deluge Daemon user"; + }; }; - users.groups.deluge.gid = config.ids.gids.deluge; + users.groups = mkIf (cfg.group == "deluge") { + deluge = { + gid = config.ids.gids.deluge; + }; + }; }; } diff --git a/nixos/modules/services/web-apps/tt-rss.nix b/nixos/modules/services/web-apps/tt-rss.nix index b882f6c2ae7..1bd9de93735 100644 --- a/nixos/modules/services/web-apps/tt-rss.nix +++ b/nixos/modules/services/web-apps/tt-rss.nix @@ -16,6 +16,9 @@ let poolName = "tt-rss"; + mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql"; + pgsqlLocal = cfg.database.createLocally && cfg.database.type == "pgsql"; + tt-rss-config = pkgs.writeText "config.php" '' <?php @@ -200,6 +203,12 @@ let and 3306 for pgsql and mysql respectively). ''; }; + + createLocally = mkOption { + type = types.bool; + default = true; + description = "Create the database and database user locally."; + }; }; auth = { @@ -551,9 +560,13 @@ let }; }; - systemd.services.tt-rss = let - dbService = if cfg.database.type == "pgsql" then "postgresql.service" else "mysql.service"; - in { + systemd.tmpfiles.rules = [ + "d '${cfg.root}' 0755 ${cfg.user} tt_rss - -" + "Z '${cfg.root}' 0755 ${cfg.user} tt_rss - -" + ]; + + systemd.services.tt-rss = + { description = "Tiny Tiny RSS feeds update daemon"; @@ -562,14 +575,14 @@ let if cfg.database.type == "pgsql" then '' ${optionalString (cfg.database.password != null) "PGPASSWORD=${cfg.database.password}"} \ ${optionalString (cfg.database.passwordFile != null) "PGPASSWORD=$(cat ${cfg.database.passwordFile})"} \ - ${pkgs.sudo}/bin/sudo -u ${cfg.user} ${config.services.postgresql.package}/bin/psql \ + ${config.services.postgresql.package}/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.sudo}/bin/sudo -u ${cfg.user} ${config.services.mysql.package}/bin/mysql \ + echo '${e}' | ${config.services.mysql.package}/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}"} \ @@ -579,7 +592,6 @@ let in '' rm -rf "${cfg.root}/*" - mkdir -m 755 -p "${cfg.root}" cp -r "${pkgs.tt-rss}/"* "${cfg.root}" ${optionalString (cfg.pluginPackages != []) '' for plugin in ${concatStringsSep " " cfg.pluginPackages}; do @@ -592,19 +604,10 @@ let done ''} 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") '' - ${optionalString (cfg.database.host == null && cfg.database.password == null) '' - if ! [ -e ${cfg.root}/.db-created ]; then - ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createuser ${cfg.database.user} - ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createdb -O ${cfg.database.user} ${cfg.database.name} - touch ${cfg.root}/.db-created - fi - ''} - exists=$(${callSql "select count(*) > 0 from pg_tables where tableowner = user"} \ | tail -n+3 | head -n-2 | sed -e 's/[ \n\t]*//') @@ -628,18 +631,18 @@ let serviceConfig = { User = "${cfg.user}"; + Group = "tt_rss"; ExecStart = "${pkgs.php}/bin/php ${cfg.root}/update.php --daemon"; StandardOutput = "syslog"; StandardError = "syslog"; - PermissionsStartOnly = true; }; wantedBy = [ "multi-user.target" ]; - requires = ["${dbService}"]; - after = ["network.target" "${dbService}"]; + requires = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service"; + after = [ "network.target" ] ++ optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service"; }; - services.mysql = optionalAttrs (cfg.database.type == "mysql") { + services.mysql = mkIf mysqlLocal { enable = true; package = mkDefault pkgs.mysql; ensureDatabases = [ cfg.database.name ]; @@ -653,17 +656,22 @@ let ]; }; - services.postgresql = optionalAttrs (cfg.database.type == "pgsql") { + services.postgresql = mkIf pgsqlLocal { enable = mkDefault true; + ensureDatabases = [ cfg.database.name ]; + ensureUsers = [ + { name = cfg.user; + ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; }; + } + ]; }; - users = optionalAttrs (cfg.user == "tt_rss") { - users.tt_rss = { - description = "tt-rss service user"; - isSystemUser = true; - group = "tt_rss"; - }; - groups.tt_rss = {}; + users.users.tt_rss = optionalAttrs (cfg.user == "tt_rss") { + description = "tt-rss service user"; + isSystemUser = true; + group = "tt_rss"; }; + + users.groups.tt_rss = {}; }; } diff --git a/nixos/modules/services/web-apps/zabbix.nix b/nixos/modules/services/web-apps/zabbix.nix new file mode 100644 index 00000000000..4b5334579a9 --- /dev/null +++ b/nixos/modules/services/web-apps/zabbix.nix @@ -0,0 +1,225 @@ +{ config, lib, pkgs, ... }: + +let + + inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption types; + inherit (lib) literalExample mapAttrs optionalString; + + cfg = config.services.zabbixWeb; + fpm = config.services.phpfpm.pools.zabbix; + + user = "zabbix"; + group = "zabbix"; + stateDir = "/var/lib/zabbix"; + + zabbixConfig = pkgs.writeText "zabbix.conf.php" '' + <?php + // Zabbix GUI configuration file. + global $DB; + $DB['TYPE'] = '${ { "mysql" = "MYSQL"; "pgsql" = "POSTGRESQL"; "oracle" = "ORACLE"; }.${cfg.database.type} }'; + $DB['SERVER'] = '${cfg.database.host}'; + $DB['PORT'] = '${toString cfg.database.port}'; + $DB['DATABASE'] = '${cfg.database.name}'; + $DB['USER'] = '${cfg.database.user}'; + $DB['PASSWORD'] = ${if cfg.database.passwordFile != null then "file_get_contents('${cfg.database.passwordFile}')" else "''"}; + // Schema name. Used for IBM DB2 and PostgreSQL. + $DB['SCHEMA'] = '''; + $ZBX_SERVER = '${cfg.server.address}'; + $ZBX_SERVER_PORT = '${toString cfg.server.port}'; + $ZBX_SERVER_NAME = '''; + $IMAGE_FORMAT_DEFAULT = IMAGE_FORMAT_PNG; + ''; + +in +{ + # interface + + options.services = { + zabbixWeb = { + enable = mkEnableOption "the Zabbix web interface"; + + package = mkOption { + type = types.package; + default = pkgs.zabbix.web; + defaultText = "zabbix.web"; + description = "Which Zabbix package to use."; + }; + + server = { + port = mkOption { + type = types.int; + description = "The port of the Zabbix server to connect to."; + default = 10051; + }; + + address = mkOption { + type = types.str; + description = "The IP address or hostname of the Zabbix server to connect to."; + default = "localhost"; + }; + }; + + database = { + type = mkOption { + type = types.enum [ "mysql" "pgsql" "oracle" ]; + example = "mysql"; + default = "pgsql"; + description = "Database engine to use."; + }; + + host = mkOption { + type = types.str; + default = ""; + description = "Database host address."; + }; + + port = mkOption { + type = types.int; + default = + if cfg.database.type == "mysql" then config.services.mysql.port + else if cfg.database.type == "pgsql" then config.services.postgresql.port + else 1521; + description = "Database host port."; + }; + + name = mkOption { + type = types.str; + default = "zabbix"; + description = "Database name."; + }; + + user = mkOption { + type = types.str; + default = "zabbix"; + description = "Database user."; + }; + + passwordFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/run/keys/zabbix-dbpassword"; + description = '' + A file containing the password corresponding to + <option>database.user</option>. + ''; + }; + + socket = mkOption { + type = types.nullOr types.path; + default = null; + example = "/run/postgresql"; + description = "Path to the unix socket file to use for authentication."; + }; + }; + + virtualHost = mkOption { + type = types.submodule ({ + options = import ../web-servers/apache-httpd/per-server-options.nix { + inherit lib; + forMainServer = false; + }; + }); + example = { + hostName = "zabbix.example.org"; + enableSSL = true; + adminAddr = "webmaster@example.org"; + sslServerCert = "/var/lib/acme/zabbix.example.org/full.pem"; + sslServerKey = "/var/lib/acme/zabbix.example.org/key.pem"; + }; + description = '' + Apache configuration can be done by adapting <literal>services.httpd.virtualHosts.<name></literal>. + See <xref linkend="opt-services.httpd.virtualHosts"/> for further information. + ''; + }; + + poolConfig = mkOption { + type = types.lines; + default = '' + pm = dynamic + pm.max_children = 32 + pm.start_servers = 2 + pm.min_spare_servers = 2 + pm.max_spare_servers = 4 + pm.max_requests = 500 + ''; + description = '' + Options for the Zabbix PHP pool. See the documentation on <literal>php-fpm.conf</literal> for details on configuration directives. + ''; + }; + + }; + }; + + # implementation + + config = mkIf cfg.enable { + + systemd.tmpfiles.rules = [ + "d '${stateDir}' 0750 ${user} ${group} - -" + "d '${stateDir}/session' 0750 ${user} ${config.services.httpd.group} - -" + ]; + + services.phpfpm.pools.zabbix = { + phpOptions = '' + # https://www.zabbix.com/documentation/current/manual/installation/install + memory_limit = 128M + post_max_size = 16M + upload_max_filesize = 2M + max_execution_time = 300 + max_input_time = 300 + session.auto_start = 0 + mbstring.func_overload = 0 + always_populate_raw_post_data = -1 + # https://bbs.archlinux.org/viewtopic.php?pid=1745214#p1745214 + session.save_path = ${stateDir}/session + '' + optionalString (config.time.timeZone != null) '' + date.timezone = "${config.time.timeZone}" + '' + optionalString (cfg.database.type == "oracle") '' + extension=${pkgs.phpPackages.oci8}/lib/php/extensions/oci8.so + ''; + listen = "/run/phpfpm/zabbix.sock"; + extraConfig = '' + listen.owner = ${config.services.httpd.user}; + listen.group = ${config.services.httpd.group}; + user = ${user}; + group = ${config.services.httpd.group}; + env[ZABBIX_CONFIG] = ${zabbixConfig} + ${cfg.poolConfig} + ''; + }; + + services.httpd = { + enable = true; + adminAddr = mkDefault cfg.virtualHost.adminAddr; + extraModules = [ "proxy_fcgi" ]; + virtualHosts = [ (mkMerge [ + cfg.virtualHost { + documentRoot = mkForce "${cfg.package}/share/zabbix"; + extraConfig = '' + <Directory "${cfg.package}/share/zabbix"> + <FilesMatch "\.php$"> + <If "-f %{REQUEST_FILENAME}"> + SetHandler "proxy:unix:${fpm.listen}|fcgi://localhost/" + </If> + </FilesMatch> + AllowOverride all + Options -Indexes + DirectoryIndex index.php + </Directory> + ''; + } + ]) ]; + }; + + users.users.${user} = mapAttrs (name: mkDefault) { + description = "Zabbix daemon user"; + uid = config.ids.uids.zabbix; + inherit group; + }; + + users.groups.${group} = mapAttrs (name: mkDefault) { + gid = config.ids.gids.zabbix; + }; + + }; +} diff --git a/nixos/modules/services/web-servers/apache-httpd/foswiki.nix b/nixos/modules/services/web-servers/apache-httpd/foswiki.nix deleted file mode 100644 index 8c1ac8935a4..00000000000 --- a/nixos/modules/services/web-servers/apache-httpd/foswiki.nix +++ /dev/null @@ -1,78 +0,0 @@ -{ config, pkgs, lib, serverInfo, ... }: -let - inherit (pkgs) foswiki; - inherit (serverInfo.serverConfig) user group; - inherit (config) vardir; -in -{ - options.vardir = lib.mkOption { - type = lib.types.path; - default = "/var/www/foswiki"; - description = "The directory where variable foswiki data will be stored and served from."; - }; - - # TODO: this will probably need to be better customizable - extraConfig = - let httpd-conf = pkgs.runCommand "foswiki-httpd.conf" - { preferLocalBuild = true; } - '' - substitute '${foswiki}/foswiki_httpd_conf.txt' "$out" \ - --replace /var/www/foswiki/ "${vardir}/" - ''; - in - '' - RewriteEngine on - RewriteRule /foswiki/(.*) ${vardir}/$1 - - <Directory "${vardir}"> - Require all granted - </Directory> - - Include ${httpd-conf} - <Directory "${vardir}/pub"> - Options FollowSymlinks - </Directory> - ''; - - /** This handles initial setup and updates. - It will probably need some tweaking, maybe per-site. */ - startupScript = pkgs.writeScript "foswiki_startup.sh" ( - let storeLink = "${vardir}/package"; in - '' - [ -e '${storeLink}' ] || needs_setup=1 - mkdir -p '${vardir}' - cd '${vardir}' - ln -sf -T '${foswiki}' '${storeLink}' - - if [ -n "$needs_setup" ]; then # do initial setup - mkdir -p bin lib - # setup most of data/ as copies only - cp -r '${foswiki}'/data '${vardir}/' - rm -r '${vardir}'/data/{System,mime.types} - ln -sr -t '${vardir}/data/' '${storeLink}'/data/{System,mime.types} - - ln -sr '${storeLink}/locale' . - - mkdir pub - ln -sr '${storeLink}/pub/System' pub/ - - mkdir templates - ln -sr '${storeLink}'/templates/* templates/ - - ln -sr '${storeLink}/tools' . - - mkdir -p '${vardir}'/working/{logs,tmp} - ln -sr '${storeLink}/working/README' working/ # used to check dir validity - - chown -R '${user}:${group}' . - chmod +w -R . - fi - - # bin/* and lib/* shall always be overwritten, in case files are added - ln -srf '${storeLink}'/bin/* '${vardir}/bin/' - ln -srf '${storeLink}'/lib/* '${vardir}/lib/' - '' - /* Symlinking bin/ one-by-one ensures that ${vardir}/lib/LocalSite.cfg - is used instead of ${foswiki}/... */ - ); -} diff --git a/nixos/modules/services/web-servers/apache-httpd/mercurial.nix b/nixos/modules/services/web-servers/apache-httpd/mercurial.nix deleted file mode 100644 index 4b8ee2b17ea..00000000000 --- a/nixos/modules/services/web-servers/apache-httpd/mercurial.nix +++ /dev/null @@ -1,75 +0,0 @@ -{ config, pkgs, lib, ... }: - -let - inherit (pkgs) mercurial; - inherit (lib) mkOption; - - urlPrefix = config.urlPrefix; - - cgi = pkgs.stdenv.mkDerivation { - name = "mercurial-cgi"; - buildCommand = '' - mkdir -p $out - cp -v ${mercurial}/share/cgi-bin/hgweb.cgi $out - sed -i "s|/path/to/repo/or/config|$out/hgweb.config|" $out/hgweb.cgi - echo " - [collections] - ${config.dataDir} = ${config.dataDir} - [web] - style = gitweb - allow_push = * - " > $out/hgweb.config - ''; - }; - -in { - - extraConfig = '' - RewriteEngine on - RewriteRule /(.*) ${cgi}/hgweb.cgi/$1 - - <Location "${urlPrefix}"> - AuthType Basic - AuthName "Mercurial repositories" - AuthUserFile ${config.dataDir}/hgusers - <LimitExcept GET> - Require valid-user - </LimitExcept> - </Location> - <Directory "${cgi}"> - Order allow,deny - Allow from all - AllowOverride All - Options ExecCGI - AddHandler cgi-script .cgi - PassEnv PYTHONPATH - </Directory> - ''; - - robotsEntries = '' - User-agent: * - Disallow: ${urlPrefix} - ''; - - extraServerPath = [ pkgs.python ]; - - globalEnvVars = [ { name = "PYTHONPATH"; value = "${mercurial}/lib/${pkgs.python.libPrefix}/site-packages"; } ]; - - options = { - urlPrefix = mkOption { - default = "/hg"; - description = " - The URL prefix under which the Mercurial service appears. - Use the empty string to have it appear in the server root. - "; - }; - - dataDir = mkOption { - example = "/data/mercurial"; - description = " - Path to the directory that holds the repositories. - "; - }; - }; - -} diff --git a/nixos/modules/services/web-servers/apache-httpd/tomcat-connector.nix b/nixos/modules/services/web-servers/apache-httpd/tomcat-connector.nix deleted file mode 100644 index a883bb2b343..00000000000 --- a/nixos/modules/services/web-servers/apache-httpd/tomcat-connector.nix +++ /dev/null @@ -1,103 +0,0 @@ -{ config, pkgs, serverInfo, lib, ... }: - -let - extraWorkersProperties = lib.optionalString (config ? extraWorkersProperties) config.extraWorkersProperties; - - workersProperties = pkgs.writeText "workers.properties" '' -# Define list of workers that will be used -# for mapping requests -# The configuration directives are valid -# for the mod_jk version 1.2.18 and later -# -worker.list=loadbalancer,status - -# Define Node1 -# modify the host as your host IP or DNS name. -worker.node1.port=8009 -worker.node1.host=localhost -worker.node1.type=ajp13 -worker.node1.lbfactor=1 - -# Load-balancing behaviour -worker.loadbalancer.type=lb -worker.loadbalancer.balance_workers=node1 - -# Status worker for managing load balancer -worker.status.type=status - -${extraWorkersProperties} - ''; -in -{ - - options = { - extraWorkersProperties = lib.mkOption { - default = ""; - description = "Additional configuration for the workers.properties file."; - }; - }; - - extraModules = [ - { name = "jk"; path = "${pkgs.tomcat_connectors}/modules/mod_jk.so"; } - ]; - - extraConfig = '' -# Where to find workers.properties -JkWorkersFile ${workersProperties} - -# Where to put jk logs -JkLogFile ${serverInfo.serverConfig.logDir}/mod_jk.log - -# Set the jk log level [debug/error/info] -JkLogLevel info - -# Select the log format -JkLogStampFormat "[%a %b %d %H:%M:%S %Y]" - -# JkOptions indicates to send SSK KEY SIZE -# Note: Changed from +ForwardURICompat. -# See http://tomcat.apache.org/security-jk.html -JkOptions +ForwardKeySize +ForwardURICompatUnparsed -ForwardDirectories - -# JkRequestLogFormat -JkRequestLogFormat "%w %V %T" - -# Mount your applications -JkMount /__application__/* loadbalancer - -# You can use external file for mount points. -# It will be checked for updates each 60 seconds. -# The format of the file is: /url=worker -# /examples/*=loadbalancer -#JkMountFile uriworkermap.properties - -# Add shared memory. -# This directive is present with 1.2.10 and -# later versions of mod_jk, and is needed for -# for load balancing to work properly -# Note: Replaced JkShmFile logs/jk.shm due to SELinux issues. Refer to -# https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=225452 -JkShmFile ${serverInfo.serverConfig.stateDir}/jk.shm - -# Static files in all Tomcat webapp context directories are served by apache -JkAutoAlias /var/tomcat/webapps - -# All requests go to worker by default -JkMount /* loadbalancer -# Serve some static files using httpd -#JkUnMount /*.html loadbalancer -#JkUnMount /*.jpg loadbalancer -#JkUnMount /*.gif loadbalancer -#JkUnMount /*.css loadbalancer -#JkUnMount /*.png loadbalancer -#JkUnMount /*.js loadbalancer - -# Add jkstatus for managing runtime data -<Location /jkstatus/> -JkMount status -Order deny,allow -Deny from all -Allow from 127.0.0.1 -</Location> - ''; -} diff --git a/nixos/modules/services/web-servers/apache-httpd/trac.nix b/nixos/modules/services/web-servers/apache-httpd/trac.nix deleted file mode 100644 index 28b411a64b6..00000000000 --- a/nixos/modules/services/web-servers/apache-httpd/trac.nix +++ /dev/null @@ -1,121 +0,0 @@ -{ config, lib, pkgs, serverInfo, ... }: - -with lib; - -let - - # Build a Subversion instance with Apache modules and Swig/Python bindings. - subversion = pkgs.subversion.override { - bdbSupport = true; - httpServer = true; - pythonBindings = true; - apacheHttpd = httpd; - }; - - httpd = serverInfo.serverConfig.package; - - versionPre24 = versionOlder httpd.version "2.4"; - -in - -{ - - options = { - - projectsLocation = mkOption { - description = "URL path in which Trac projects can be accessed"; - default = "/projects"; - }; - - projects = mkOption { - description = "List of projects that should be provided by Trac. If they are not defined yet empty projects are created."; - default = []; - example = - [ { identifier = "myproject"; - name = "My Project"; - databaseURL="postgres://root:password@/tracdb"; - subversionRepository="/data/subversion/myproject"; - } - ]; - }; - - user = mkOption { - default = "wwwrun"; - description = "User account under which Trac runs."; - }; - - group = mkOption { - default = "wwwrun"; - description = "Group under which Trac runs."; - }; - - ldapAuthentication = { - enable = mkOption { - default = false; - description = "Enable the ldap authentication in trac"; - }; - - url = mkOption { - default = "ldap://127.0.0.1/dc=example,dc=co,dc=ke?uid?sub?(objectClass=inetOrgPerson)"; - description = "URL of the LDAP authentication"; - }; - - name = mkOption { - default = "Trac server"; - description = "AuthName"; - }; - }; - - }; - - extraModules = singleton - { name = "python"; path = "${pkgs.mod_python}/modules/mod_python.so"; }; - - extraConfig = '' - <Location ${config.projectsLocation}> - SetHandler mod_python - PythonHandler trac.web.modpython_frontend - PythonOption TracEnvParentDir /var/trac/projects - PythonOption TracUriRoot ${config.projectsLocation} - PythonOption PYTHON_EGG_CACHE /var/trac/egg-cache - </Location> - ${if config.ldapAuthentication.enable then '' - <LocationMatch "^${config.projectsLocation}[^/]+/login$"> - AuthType Basic - AuthName "${config.ldapAuthentication.name}" - AuthBasicProvider "ldap" - AuthLDAPURL "${config.ldapAuthentication.url}" - ${if versionPre24 then "authzldapauthoritative Off" else ""} - require valid-user - </LocationMatch> - '' else ""} - ''; - - globalEnvVars = singleton - { name = "PYTHONPATH"; - value = - makeSearchPathOutput "lib" "lib/${pkgs.python.libPrefix}/site-packages" - [ pkgs.mod_python - pkgs.pythonPackages.trac - pkgs.pythonPackages.setuptools - pkgs.pythonPackages.genshi - pkgs.pythonPackages.psycopg2 - subversion - ]; - }; - - startupScript = pkgs.writeScript "activateTrac" '' - mkdir -p /var/trac - chown ${config.user}:${config.group} /var/trac - - ${concatMapStrings (project: - '' - if [ ! -d /var/trac/${project.identifier} ] - then - export PYTHONPATH=${pkgs.pythonPackages.psycopg2}/lib/${pkgs.python.libPrefix}/site-packages - ${pkgs.pythonPackages.trac}/bin/trac-admin /var/trac/${project.identifier} initenv "${project.name}" "${project.databaseURL}" svn "${project.subversionRepository}" - fi - '' ) (config.projects)} - ''; - -} diff --git a/nixos/modules/services/web-servers/apache-httpd/zabbix.nix b/nixos/modules/services/web-servers/apache-httpd/zabbix.nix deleted file mode 100644 index cab16593bcb..00000000000 --- a/nixos/modules/services/web-servers/apache-httpd/zabbix.nix +++ /dev/null @@ -1,84 +0,0 @@ -{ config, lib, pkgs, serverInfo, ... }: - -with lib; - -let - - # The Zabbix PHP frontend needs to be able to write its - # configuration settings (the connection info to the database) to - # the "conf" subdirectory. So symlink $out/conf to some directory - # outside of the Nix store where we want to keep this stateful info. - # Note that different instances of the frontend will therefore end - # up with their own copies of the PHP sources. !!! Alternatively, - # we could generate zabbix.conf.php declaratively. - zabbixPHP = pkgs.runCommand "${pkgs.zabbix.server.name}-php" {} - '' - cp -rs ${pkgs.zabbix.server}/share/zabbix/php "$out" - chmod -R u+w $out - ln -s "${if config.configFile == null - then "${config.stateDir}/zabbix.conf.php" - else config.configFile}" "$out/conf/zabbix.conf.php" - ''; - -in - -{ - - enablePHP = true; - - phpOptions = - '' - post_max_size = 32M - max_execution_time = 300 - max_input_time = 300 - ''; - - extraConfig = '' - Alias ${config.urlPrefix}/ ${zabbixPHP}/ - - <Directory ${zabbixPHP}> - DirectoryIndex index.php - Order deny,allow - Allow from * - </Directory> - ''; - - startupScript = pkgs.writeScript "zabbix-startup-hook" '' - mkdir -p ${config.stateDir} - chown -R ${serverInfo.serverConfig.user} ${config.stateDir} - ''; - - # The frontend needs "ps" to find out whether zabbix_server is running. - extraServerPath = [ pkgs.procps ]; - - options = { - - urlPrefix = mkOption { - default = "/zabbix"; - description = " - The URL prefix under which the Zabbix service appears. - Use the empty string to have it appear in the server root. - "; - }; - - configFile = mkOption { - default = null; - type = types.nullOr types.path; - description = '' - The configuration file (zabbix.conf.php) which contains the database - connection settings. If not set, the configuration settings will created - by the web installer. - ''; - }; - - stateDir = mkOption { - default = "/var/lib/zabbix/frontend"; - description = " - Directory where the dynamically generated configuration data - of the PHP frontend will be stored. - "; - }; - - }; - -} diff --git a/nixos/modules/services/web-servers/lighttpd/collectd.nix b/nixos/modules/services/web-servers/lighttpd/collectd.nix index e70c980d524..3f262451c2c 100644 --- a/nixos/modules/services/web-servers/lighttpd/collectd.nix +++ b/nixos/modules/services/web-servers/lighttpd/collectd.nix @@ -12,7 +12,7 @@ let defaultCollectionCgi = config.services.collectd.package.overrideDerivation(old: { name = "collection.cgi"; - configurePhase = "true"; + dontConfigure = true; buildPhase = "true"; installPhase = '' substituteInPlace contrib/collection.cgi --replace '"/etc/collection.conf"' '$ENV{COLLECTION_CONF}' diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix index ef6820d3326..4a7a4804e1a 100644 --- a/nixos/modules/services/x11/desktop-managers/gnome3.nix +++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix @@ -154,7 +154,7 @@ in { services.hardware.bolt.enable = mkDefault true; services.xserver.libinput.enable = mkDefault true; # for controlling touchpad settings via gnome control center systemd.packages = [ pkgs.gnome3.vino ]; - services.flatpak.extraPortals = [ pkgs.xdg-desktop-portal-gtk ]; + xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-gtk ]; # If gnome3 is installed, build vim for gtk3 too. nixpkgs.config.vim.gui = "gtk3"; diff --git a/nixos/modules/services/x11/desktop-managers/pantheon.nix b/nixos/modules/services/x11/desktop-managers/pantheon.nix index 41903b33fae..c0eae1eb8d4 100644 --- a/nixos/modules/services/x11/desktop-managers/pantheon.nix +++ b/nixos/modules/services/x11/desktop-managers/pantheon.nix @@ -145,6 +145,8 @@ in isSystem = true; }; + xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-gtk ]; + networking.networkmanager.enable = mkDefault true; networking.networkmanager.basePackages = { inherit (pkgs) networkmanager modemmanager wpa_supplicant; diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix index dc8bfc7dc17..98c9ae86cee 100644 --- a/nixos/modules/services/x11/desktop-managers/plasma5.nix +++ b/nixos/modules/services/x11/desktop-managers/plasma5.nix @@ -21,6 +21,13 @@ in description = "Enable the Plasma 5 (KDE 5) desktop environment."; }; + phononBackend = mkOption { + type = types.enum [ "gstreamer" "vlc" ]; + default = "gstreamer"; + example = "vlc"; + description = "Phonon audio backend to install."; + }; + enableQt4Support = mkOption { type = types.bool; default = true; @@ -64,8 +71,8 @@ in }; security.wrappers = { - kcheckpass.source = "${lib.getBin plasma5.kscreenlocker}/lib/libexec/kcheckpass"; - "start_kdeinit".source = "${lib.getBin pkgs.kinit}/lib/libexec/kf5/start_kdeinit"; + kcheckpass.source = "${lib.getBin plasma5.kscreenlocker}/libexec/kcheckpass"; + "start_kdeinit".source = "${lib.getBin pkgs.kinit}/libexec/kf5/start_kdeinit"; kwin_wayland = { source = "${lib.getBin plasma5.kwin}/bin/kwin_wayland"; capabilities = "cap_sys_nice+ep"; @@ -161,12 +168,14 @@ in qtvirtualkeyboard - libsForQt5.phonon-backend-gstreamer - xdg-user-dirs # Update user dirs as described in https://freedesktop.org/wiki/Software/xdg-user-dirs/ ] - - ++ lib.optionals cfg.enableQt4Support [ pkgs.phonon-backend-gstreamer ] + + # Phonon audio backend + ++ lib.optional (cfg.phononBackend == "gstreamer") libsForQt5.phonon-backend-gstreamer + ++ lib.optional (cfg.phononBackend == "gstreamer" && cfg.enableQt4Support) pkgs.phonon-backend-gstreamer + ++ lib.optional (cfg.phononBackend == "vlc") libsForQt5.phonon-backend-vlc + ++ lib.optional (cfg.phononBackend == "vlc" && cfg.enableQt4Support) pkgs.phonon-backend-vlc # Optional hardware support features ++ lib.optional config.hardware.bluetooth.enable bluedevil @@ -224,6 +233,8 @@ in security.pam.services.sddm.enableKwallet = true; security.pam.services.slim.enableKwallet = true; + xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-kde ]; + # Update the start menu for each user that is currently logged in system.userActivationScripts.plasmaSetup = '' # The KDE icon cache is supposed to update itself diff --git a/nixos/modules/services/x11/hardware/libinput.nix b/nixos/modules/services/x11/hardware/libinput.nix index 58fe702d496..a0a5e265685 100644 --- a/nixos/modules/services/x11/hardware/libinput.nix +++ b/nixos/modules/services/x11/hardware/libinput.nix @@ -178,7 +178,7 @@ in { }; additionalOptions = mkOption { - type = types.str; + type = types.lines; default = ""; example = '' diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix index a1ed2fd1e97..82730c5e80c 100644 --- a/nixos/modules/services/x11/xserver.nix +++ b/nixos/modules/services/x11/xserver.nix @@ -662,10 +662,9 @@ in restartIfChanged = false; environment = - { - LD_LIBRARY_PATH = concatStringsSep ":" ([ "/run/opengl-driver/lib" ] - ++ concatLists (catAttrs "libPath" cfg.drivers)); - } // cfg.displayManager.job.environment; + optionalAttrs config.hardware.opengl.setLdLibraryPath + { LD_LIBRARY_PATH = pkgs.addOpenGLRunpath.driverLink; } + // cfg.displayManager.job.environment; preStart = '' |