diff options
Diffstat (limited to 'nixos')
-rw-r--r-- | nixos/doc/manual/default.nix | 71 | ||||
-rw-r--r-- | nixos/modules/config/nsswitch.nix | 40 | ||||
-rw-r--r-- | nixos/modules/misc/ids.nix | 2 | ||||
-rw-r--r-- | nixos/modules/module-list.nix | 2 | ||||
-rw-r--r-- | nixos/modules/programs/zsh/zsh.nix | 2 | ||||
-rw-r--r-- | nixos/modules/security/wrappers/default.nix | 2 | ||||
-rw-r--r-- | nixos/modules/services/continuous-integration/hydra/default.nix | 3 | ||||
-rw-r--r-- | nixos/modules/services/databases/mysql.nix | 8 | ||||
-rw-r--r-- | nixos/modules/services/databases/rethinkdb.nix | 110 | ||||
-rw-r--r-- | nixos/modules/services/monitoring/prometheus/default.nix | 9 | ||||
-rw-r--r-- | nixos/modules/services/networking/networkmanager.nix | 2 | ||||
-rw-r--r-- | nixos/modules/services/networking/toxvpn.nix | 16 | ||||
-rw-r--r-- | nixos/modules/services/web-apps/piwik-doc.xml | 97 | ||||
-rw-r--r-- | nixos/modules/services/web-apps/piwik.nix | 219 | ||||
-rw-r--r-- | nixos/modules/services/web-servers/minio.nix | 69 | ||||
-rw-r--r-- | nixos/modules/services/x11/xserver.nix | 10 | ||||
-rw-r--r-- | nixos/release-combined.nix | 1 | ||||
-rw-r--r-- | nixos/tests/installer.nix | 39 | ||||
-rw-r--r-- | nixos/tests/minio.nix | 19 |
19 files changed, 657 insertions, 64 deletions
diff --git a/nixos/doc/manual/default.nix b/nixos/doc/manual/default.nix index 40d49f1541b3..9413d71a34cf 100644 --- a/nixos/doc/manual/default.nix +++ b/nixos/doc/manual/default.nix @@ -65,7 +65,7 @@ let chmod -R u+w . ln -s ${modulesDoc} configuration/modules.xml ln -s ${optionsDocBook} options-db.xml - echo "${version}" > version + printf "%s" "${version}" > version ''; toc = builtins.toFile "toc.xml" @@ -94,25 +94,43 @@ let "--stringparam chunk.toc ${toc}" ]; - olinkDB = runCommand "manual-olinkdb" + manual-combined = runCommand "nixos-manual-combined" { inherit sources; buildInputs = [ libxml2 libxslt ]; + meta.description = "The NixOS manual as plain docbook XML"; } '' ${copySources} + xmllint --xinclude --output ./manual-combined.xml ./manual.xml + xmllint --xinclude --noxincludenode \ + --output ./man-pages-combined.xml ./man-pages.xml + + xmllint --debug --noout --nonet \ + --relaxng ${docbook5}/xml/rng/docbook/docbook.rng \ + manual-combined.xml + xmllint --debug --noout --nonet \ + --relaxng ${docbook5}/xml/rng/docbook/docbook.rng \ + man-pages-combined.xml + + + mkdir $out + cp manual-combined.xml $out/ + cp man-pages-combined.xml $out/ + ''; + + olinkDB = runCommand "manual-olinkdb" + { inherit sources; + buildInputs = [ libxml2 libxslt ]; + } + '' xsltproc \ ${manualXsltprocOptions} \ --stringparam collect.xref.targets only \ --stringparam targets.filename "$out/manual.db" \ - --nonet --xinclude \ + --nonet \ ${docbook5_xsl}/xml/xsl/docbook/xhtml/chunktoc.xsl \ - ./manual.xml - - # Check the validity of the man pages sources. - xmllint --noout --nonet --xinclude --noxincludenode \ - --relaxng ${docbook5}/xml/rng/docbook/docbook.rng \ - ./man-pages.xml + ${manual-combined}/manual-combined.xml cat > "$out/olinkdb.xml" <<EOF <?xml version="1.0" encoding="utf-8"?> @@ -158,21 +176,15 @@ in rec { allowedReferences = ["out"]; } '' - ${copySources} - - # Check the validity of the manual sources. - xmllint --noout --nonet --xinclude --noxincludenode \ - --relaxng ${docbook5}/xml/rng/docbook/docbook.rng \ - manual.xml - # Generate the HTML manual. dst=$out/share/doc/nixos mkdir -p $dst xsltproc \ ${manualXsltprocOptions} \ --stringparam target.database.document "${olinkDB}/olinkdb.xml" \ - --nonet --xinclude --output $dst/ \ - ${docbook5_xsl}/xml/xsl/docbook/xhtml/chunktoc.xsl ./manual.xml + --nonet --output $dst/ \ + ${docbook5_xsl}/xml/xsl/docbook/xhtml/chunktoc.xsl \ + ${manual-combined}/manual-combined.xml mkdir -p $dst/images/callouts cp ${docbook5_xsl}/xml/xsl/docbook/images/callouts/*.gif $dst/images/callouts/ @@ -190,13 +202,6 @@ in rec { buildInputs = [ libxml2 libxslt zip ]; } '' - ${copySources} - - # Check the validity of the manual sources. - xmllint --noout --nonet --xinclude --noxincludenode \ - --relaxng ${docbook5}/xml/rng/docbook/docbook.rng \ - manual.xml - # Generate the epub manual. dst=$out/share/doc/nixos @@ -204,10 +209,11 @@ in rec { ${manualXsltprocOptions} \ --stringparam target.database.document "${olinkDB}/olinkdb.xml" \ --nonet --xinclude --output $dst/epub/ \ - ${docbook5_xsl}/xml/xsl/docbook/epub/docbook.xsl ./manual.xml + ${docbook5_xsl}/xml/xsl/docbook/epub/docbook.xsl \ + ${manual-combined}/manual-combined.xml mkdir -p $dst/epub/OEBPS/images/callouts - cp -r ${docbook5_xsl}/xml/xsl/docbook/images/callouts/*.gif $dst/epub/OEBPS/images/callouts + cp -r ${docbook5_xsl}/xml/xsl/docbook/images/callouts/*.gif $dst/epub/OEBPS/images/callouts # */ echo "application/epub+zip" > mimetype manual="$dst/nixos-manual.epub" zip -0Xq "$manual" mimetype @@ -227,23 +233,16 @@ in rec { allowedReferences = ["out"]; } '' - ${copySources} - - # Check the validity of the man pages sources. - xmllint --noout --nonet --xinclude --noxincludenode \ - --relaxng ${docbook5}/xml/rng/docbook/docbook.rng \ - ./man-pages.xml - # Generate manpages. mkdir -p $out/share/man - xsltproc --nonet --xinclude \ + xsltproc --nonet \ --param man.output.in.separate.dir 1 \ --param man.output.base.dir "'$out/share/man/'" \ --param man.endnotes.are.numbered 0 \ --param man.break.after.slash 1 \ --stringparam target.database.document "${olinkDB}/olinkdb.xml" \ ${docbook5_xsl}/xml/xsl/docbook/manpages/docbook.xsl \ - ./man-pages.xml + ${manual-combined}/man-pages-combined.xml ''; } diff --git a/nixos/modules/config/nsswitch.nix b/nixos/modules/config/nsswitch.nix index d541fff140eb..16c43a99ad56 100644 --- a/nixos/modules/config/nsswitch.nix +++ b/nixos/modules/config/nsswitch.nix @@ -6,24 +6,29 @@ with lib; let - inherit (config.services.avahi) nssmdns; - inherit (config.services.samba) nsswins; - ldap = (config.users.ldap.enable && config.users.ldap.nsswitch); - sssd = config.services.sssd.enable; - resolved = config.services.resolved.enable; - - hostArray = [ "files" "mymachines" ] + # only with nscd up and running we can load NSS modules that are not integrated in NSS + canLoadExternalModules = config.services.nscd.enable; + myhostname = canLoadExternalModules; + mymachines = canLoadExternalModules; + nssmdns = canLoadExternalModules && config.services.avahi.nssmdns; + nsswins = canLoadExternalModules && config.services.samba.nsswins; + ldap = canLoadExternalModules && (config.users.ldap.enable && config.users.ldap.nsswitch); + sssd = canLoadExternalModules && config.services.sssd.enable; + resolved = canLoadExternalModules && config.services.resolved.enable; + + hostArray = [ "files" ] + ++ optionals mymachines [ "mymachines" ] ++ optionals nssmdns [ "mdns_minimal [!UNAVAIL=return]" ] ++ optionals nsswins [ "wins" ] - ++ optionals resolved ["resolv [!UNAVAIL=return]"] + ++ optionals resolved ["resolve [!UNAVAIL=return]"] ++ [ "dns" ] ++ optionals nssmdns [ "mdns" ] - ++ ["myhostname" ]; + ++ optionals myhostname ["myhostname" ]; passwdArray = [ "files" ] ++ optional sssd "sss" ++ optionals ldap [ "ldap" ] - ++ [ "mymachines" ]; + ++ optionals mymachines [ "mymachines" ]; shadowArray = [ "files" ] ++ optional sssd "sss" @@ -36,6 +41,7 @@ in { options = { # NSS modules. Hacky! + # Only works with nscd! system.nssModules = mkOption { type = types.listOf types.path; internal = true; @@ -55,6 +61,18 @@ in { }; config = { + assertions = [ + { + # generic catch if the NixOS module adding to nssModules does not prevent it with specific message. + assertion = config.system.nssModules.path != "" -> canLoadExternalModules; + message = "Loading NSS modules from path ${config.system.nssModules.path} requires nscd being enabled."; + } + { + # resolved does not need to add to nssModules, therefore needs an extra assertion + assertion = resolved -> canLoadExternalModules; + message = "Loading systemd-resolved's nss-resolve NSS module requires nscd being enabled."; + } + ]; # Name Service Switch configuration file. Required by the C # library. !!! Factor out the mdns stuff. The avahi module @@ -78,7 +96,7 @@ in { # configured IP addresses, or ::1 and 127.0.0.2 as # fallbacks. Systemd also provides nss-mymachines to return IP # addresses of local containers. - system.nssModules = [ config.systemd.package.out ]; + system.nssModules = optionals canLoadExternalModules [ config.systemd.package.out ]; }; } diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix index d7459e3fe91c..22059bb7fbbb 100644 --- a/nixos/modules/misc/ids.nix +++ b/nixos/modules/misc/ids.nix @@ -295,6 +295,7 @@ aria2 = 277; clickhouse = 278; rslsync = 279; + minio = 280; # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399! @@ -559,6 +560,7 @@ aria2 = 277; clickhouse = 278; rslsync = 279; + minio = 280; # When adding a gid, make sure it doesn't match an existing # uid. Users and groups with the same name should have equal diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 07be6555b233..59419a5e8c56 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -576,6 +576,7 @@ ./services/web-apps/frab.nix ./services/web-apps/mattermost.nix ./services/web-apps/nixbot.nix + ./services/web-apps/piwik.nix ./services/web-apps/pump.io.nix ./services/web-apps/tt-rss.nix ./services/web-apps/selfoss.nix @@ -588,6 +589,7 @@ ./services/web-servers/lighttpd/default.nix ./services/web-servers/lighttpd/gitweb.nix ./services/web-servers/lighttpd/inginious.nix + ./services/web-servers/minio.nix ./services/web-servers/nginx/default.nix ./services/web-servers/phpfpm/default.nix ./services/web-servers/shellinabox.nix diff --git a/nixos/modules/programs/zsh/zsh.nix b/nixos/modules/programs/zsh/zsh.nix index acb3e987aee6..b276bf9bb73c 100644 --- a/nixos/modules/programs/zsh/zsh.nix +++ b/nixos/modules/programs/zsh/zsh.nix @@ -117,7 +117,7 @@ in # Tell zsh how to find installed completions for p in ''${(z)NIX_PROFILES}; do - fpath+=($p/share/zsh/site-functions $p/share/zsh/$ZSH_VERSION/functions) + fpath+=($p/share/zsh/site-functions $p/share/zsh/$ZSH_VERSION/functions $p/share/zsh/vendor-completions) done ${if cfg.enableCompletion then "autoload -U compinit && compinit" else ""} diff --git a/nixos/modules/security/wrappers/default.nix b/nixos/modules/security/wrappers/default.nix index dc1227c5628d..a6dc8faaae98 100644 --- a/nixos/modules/security/wrappers/default.nix +++ b/nixos/modules/security/wrappers/default.nix @@ -171,7 +171,7 @@ in ###### setcap activation script system.activationScripts.wrappers = - lib.stringAfter [ "users" ] + lib.stringAfter [ "specialfs" "users" ] '' # Look in the system path and in the default profile for # programs to be wrapped. diff --git a/nixos/modules/services/continuous-integration/hydra/default.nix b/nixos/modules/services/continuous-integration/hydra/default.nix index 6d39affee480..fcc0f58637c4 100644 --- a/nixos/modules/services/continuous-integration/hydra/default.nix +++ b/nixos/modules/services/continuous-integration/hydra/default.nix @@ -308,6 +308,7 @@ in requires = [ "hydra-init.service" ]; after = [ "hydra-init.service" ]; environment = serverEnv; + restartTriggers = [ hydraConf ]; serviceConfig = { ExecStart = "@${cfg.package}/bin/hydra-server hydra-server -f -h '${cfg.listenHost}' " @@ -324,6 +325,7 @@ in requires = [ "hydra-init.service" ]; after = [ "hydra-init.service" "network.target" ]; path = [ cfg.package pkgs.nettools pkgs.openssh pkgs.bzip2 config.nix.package ]; + restartTriggers = [ hydraConf ]; environment = env // { PGPASSFILE = "${baseDir}/pgpass-queue-runner"; # grrr IN_SYSTEMD = "1"; # to get log severity levels @@ -345,6 +347,7 @@ in requires = [ "hydra-init.service" ]; after = [ "hydra-init.service" "network.target" ]; path = with pkgs; [ cfg.package nettools jq ]; + restartTriggers = [ hydraConf ]; environment = env; serviceConfig = { ExecStart = "@${cfg.package}/bin/hydra-evaluator hydra-evaluator"; diff --git a/nixos/modules/services/databases/mysql.nix b/nixos/modules/services/databases/mysql.nix index bae6b170c472..6027f109285a 100644 --- a/nixos/modules/services/databases/mysql.nix +++ b/nixos/modules/services/databases/mysql.nix @@ -20,6 +20,7 @@ let '' [mysqld] port = ${toString cfg.port} + ${optionalString (cfg.bind != null) "bind-address = ${cfg.bind}" } ${optionalString (cfg.replication.role == "master" || cfg.replication.role == "slave") "log-bin=mysql-bin"} ${optionalString (cfg.replication.role == "master" || cfg.replication.role == "slave") "server-id = ${toString cfg.replication.serverId}"} ${optionalString (cfg.replication.role == "slave" && !atLeast55) @@ -58,6 +59,13 @@ in "; }; + bind = mkOption { + type = types.nullOr types.str; + default = null; + example = literalExample "0.0.0.0"; + description = "Address to bind to. The default it to bind to all addresses"; + }; + port = mkOption { type = types.int; default = 3306; diff --git a/nixos/modules/services/databases/rethinkdb.nix b/nixos/modules/services/databases/rethinkdb.nix new file mode 100644 index 000000000000..cd8c386b08db --- /dev/null +++ b/nixos/modules/services/databases/rethinkdb.nix @@ -0,0 +1,110 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.rethinkdb; + rethinkdb = cfg.package; +in + +{ + + ###### interface + + options = { + + services.rethinkdb = { + + enable = mkOption { + default = false; + description = "Whether to enable the RethinkDB server."; + }; + + #package = mkOption { + # default = pkgs.rethinkdb; + # description = "Which RethinkDB derivation to use."; + #}; + + user = mkOption { + default = "rethinkdb"; + description = "User account under which RethinkDB runs."; + }; + + group = mkOption { + default = "rethinkdb"; + description = "Group which rethinkdb user belongs to."; + }; + + dbpath = mkOption { + default = "/var/db/rethinkdb"; + description = "Location where RethinkDB stores its data, 1 data directory per instance."; + }; + + pidpath = mkOption { + default = "/var/run/rethinkdb"; + description = "Location where each instance's pid file is located."; + }; + + #cfgpath = mkOption { + # default = "/etc/rethinkdb/instances.d"; + # description = "Location where RethinkDB stores it config files, 1 config file per instance."; + #}; + + # TODO: currently not used by our implementation. + #instances = mkOption { + # type = types.attrsOf types.str; + # default = {}; + # description = "List of named RethinkDB instances in our cluster."; + #}; + + }; + + }; + + ###### implementation + config = mkIf config.services.rethinkdb.enable { + + environment.systemPackages = [ rethinkdb ]; + + systemd.services.rethinkdb = { + description = "RethinkDB server"; + + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + # TODO: abstract away 'default', which is a per-instance directory name + # allowing end user of this nix module to provide multiple instances, + # and associated directory per instance + ExecStart = "${rethinkdb}/bin/rethinkdb -d ${cfg.dbpath}/default"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + User = cfg.user; + Group = cfg.group; + PIDFile = "${cfg.pidpath}/default.pid"; + PermissionsStartOnly = true; + }; + + preStart = '' + if ! test -e ${cfg.dbpath}; then + install -d -m0755 -o ${cfg.user} -g ${cfg.group} ${cfg.dbpath} + install -d -m0755 -o ${cfg.user} -g ${cfg.group} ${cfg.dbpath}/default + chown -R ${cfg.user}:${cfg.group} ${cfg.dbpath} + fi + if ! test -e "${cfg.pidpath}/default.pid"; then + install -D -o ${cfg.user} -g ${cfg.group} /dev/null "${cfg.pidpath}/default.pid" + fi + ''; + }; + + users.extraUsers.rethinkdb = mkIf (cfg.user == "rethinkdb") + { name = "rethinkdb"; + description = "RethinkDB server user"; + }; + + users.extraGroups = optionalAttrs (cfg.group == "rethinkdb") (singleton + { name = "rethinkdb"; + }); + + }; + +} diff --git a/nixos/modules/services/monitoring/prometheus/default.nix b/nixos/modules/services/monitoring/prometheus/default.nix index b9435c02b1de..c12b5f35dea3 100644 --- a/nixos/modules/services/monitoring/prometheus/default.nix +++ b/nixos/modules/services/monitoring/prometheus/default.nix @@ -66,15 +66,6 @@ let How frequently to evaluate rules by default. ''; }; - - labels = mkOption { - type = types.attrsOf types.str; - default = {}; - description = '' - The labels to add to any timeseries that this Prometheus instance - scrapes. - ''; - }; }; }; diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix index f8b7f26f5f2f..58c93d8e2ac3 100644 --- a/nixos/modules/services/networking/networkmanager.nix +++ b/nixos/modules/services/networking/networkmanager.nix @@ -16,8 +16,6 @@ let dns=${if cfg.useDnsmasq then "dnsmasq" else "default"} [keyfile] - ${optionalString (config.networking.hostName != "") - ''hostname=${config.networking.hostName}''} ${optionalString (cfg.unmanaged != []) ''unmanaged-devices=${lib.concatStringsSep ";" cfg.unmanaged}''} diff --git a/nixos/modules/services/networking/toxvpn.nix b/nixos/modules/services/networking/toxvpn.nix index 911836fdee42..5e13402d7645 100644 --- a/nixos/modules/services/networking/toxvpn.nix +++ b/nixos/modules/services/networking/toxvpn.nix @@ -18,6 +18,13 @@ with lib; default = 33445; description = "udp port for toxcore, port-forward to help with connectivity if you run many nodes behind one NAT"; }; + + auto_add_peers = mkOption { + type = types.listOf types.string; + default = []; + example = ''[ "toxid1" "toxid2" ]''; + description = "peers to automacally connect to on startup"; + }; }; }; @@ -33,8 +40,13 @@ with lib; chown toxvpn /run/toxvpn ''; + path = [ pkgs.toxvpn ]; + + script = '' + exec toxvpn -i ${config.services.toxvpn.localip} -l /run/toxvpn/control -u toxvpn -p ${toString config.services.toxvpn.port} ${lib.concatMapStringsSep " " (x: "-a ${x}") config.services.toxvpn.auto_add_peers} + ''; + serviceConfig = { - ExecStart = "${pkgs.toxvpn}/bin/toxvpn -i ${config.services.toxvpn.localip} -l /run/toxvpn/control -u toxvpn -p ${toString config.services.toxvpn.port}"; KillMode = "process"; Restart = "on-success"; Type = "notify"; @@ -43,6 +55,8 @@ with lib; restartIfChanged = false; # Likely to be used for remote admin }; + environment.systemPackages = [ pkgs.toxvpn ]; + users.extraUsers = { toxvpn = { uid = config.ids.uids.toxvpn; diff --git a/nixos/modules/services/web-apps/piwik-doc.xml b/nixos/modules/services/web-apps/piwik-doc.xml new file mode 100644 index 000000000000..a1d8a5b7556a --- /dev/null +++ b/nixos/modules/services/web-apps/piwik-doc.xml @@ -0,0 +1,97 @@ +<chapter xmlns="http://docbook.org/ns/docbook" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:xi="http://www.w3.org/2001/XInclude" + version="5.0" + xml:id="module-services-piwik"> + + <title>Piwik</title> + <para> + Piwik is a real-time web analytics application. + This module configures php-fpm as backend for piwik, optionally configuring an nginx vhost as well. + </para> + + <para> + An automatic setup is not suported by piwik, so you need to configure piwik itself in the browser-based piwik setup. + </para> + + + <section> + <title>Database Setup</title> + + <para> + You also need to configure a MariaDB or MySQL database and -user for piwik yourself, + and enter those credentials in your browser. + You can use passwordless database authentication via the UNIX_SOCKET authentication plugin + with the following SQL commands: + <programlisting> + INSTALL PLUGIN unix_socket SONAME 'auth_socket'; + ALTER USER root IDENTIFIED VIA unix_socket; + CREATE DATABASE piwik; + CREATE USER 'piwik'@'localhost' IDENTIFIED VIA unix_socket; + GRANT ALL PRIVILEGES ON piwik.* TO 'piwik'@'localhost'; + </programlisting> + Then fill in <literal>piwik</literal> as database user and database name, and leave the password field blank. + This works with MariaDB and MySQL. This authentication works by allowing only the <literal>piwik</literal> unix + user to authenticate as <literal>piwik</literal> database (without needing a password), but no other users. + For more information on passwordless login, see + <link xlink:href="https://mariadb.com/kb/en/mariadb/unix_socket-authentication-plugin/" />. + </para> + + <para> + Of course, you can use password based authentication as well, e.g. when the database is not on the same host. + </para> + </section> + + + <section> + <title>Backup</title> + <para> + You only need to take backups of your MySQL database and the + <filename>/var/lib/piwik/config/config.ini.php</filename> file. + Use a user in the <literal>piwik</literal> group or root to access the file. + For more information, see <link xlink:href="https://piwik.org/faq/how-to-install/faq_138/" />. + </para> + </section> + + + <section> + <title>Issues</title> + <itemizedlist> + <listitem> + <para> + Piwik's file integrity check will warn you. + This is due to the patches necessary for NixOS, you can safely ignore this. + </para> + </listitem> + + <listitem> + <para> + Piwik will warn you that the JavaScript tracker is not writable. + This is because it's located in the read-only nix store. + You can safely ignore this, unless you need a plugin that needs JavaScript tracker access. + </para> + </listitem> + + <listitem> + <para> + Sending mail from piwik, e.g. for the password reset function, might not work out of the box: + There's a problem with using <command>sendmail</command> from <literal>php-fpm</literal> that is + being investigated at <link xlink:href="https://github.com/NixOS/nixpkgs/issues/26611" />. + If you have (or don't have) this problem as well, please report it. You can enable SMTP as method + to send mail in piwik's <quote>General Settings</quote> > <quote>Mail Server Settings</quote> instead. + </para> + </listitem> + </itemizedlist> + </section> + + + <section> + <title>Using other Web Servers than nginx</title> + + <para> + You can use other web servers by forwarding calls for <filename>index.php</filename> and + <filename>piwik.php</filename> to the <literal>/run/phpfpm-piwik.sock</literal> fastcgi unix socket. + You can use the nginx configuration in the module code as a reference to what else should be configured. + </para> + </section> +</chapter> diff --git a/nixos/modules/services/web-apps/piwik.nix b/nixos/modules/services/web-apps/piwik.nix new file mode 100644 index 000000000000..26342a9c5f00 --- /dev/null +++ b/nixos/modules/services/web-apps/piwik.nix @@ -0,0 +1,219 @@ +{ config, lib, pkgs, services, ... }: +with lib; +let + cfg = config.services.piwik; + + user = "piwik"; + dataDir = "/var/lib/${user}"; + + pool = user; + # it's not possible to use /run/phpfpm/${pool}.sock because /run/phpfpm/ is root:root 0770, + # and therefore is not accessible by the web server. + phpSocket = "/run/phpfpm-${pool}.sock"; + phpExecutionUnit = "phpfpm-${pool}"; + databaseService = "mysql.service"; + +in { + options = { + services.piwik = { + # NixOS PR for database setup: https://github.com/NixOS/nixpkgs/pull/6963 + # piwik issue for automatic piwik setup: https://github.com/piwik/piwik/issues/10257 + # TODO: find a nice way to do this when more NixOS MySQL and / or piwik automatic setup stuff is implemented. + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enable piwik web analytics with php-fpm backend. + ''; + }; + + webServerUser = mkOption { + type = types.str; + example = "nginx"; + description = '' + Name of the owner of the ${phpSocket} fastcgi socket for piwik. + If you want to use another webserver than nginx, you need to set this to that server's user + and pass fastcgi requests to `index.php` and `piwik.php` to this socket. + ''; + }; + + phpfpmProcessManagerConfig = mkOption { + type = types.str; + default = '' + ; default phpfpm process manager settings + pm = dynamic + pm.max_children = 75 + pm.start_servers = 10 + pm.min_spare_servers = 5 + pm.max_spare_servers = 20 + pm.max_requests = 500 + + ; log worker's stdout, but this has a performance hit + catch_workers_output = yes + ''; + description = '' + Settings for phpfpm's process manager. You might need to change this depending on the load for piwik. + ''; + }; + + nginx = mkOption { + # TODO: for maximum flexibility, it would be nice to use nginx's vhost_options module + # but this only makes sense if we can somehow specify defaults suitable for piwik. + # But users can always copy the piwik nginx config to their configuration.nix and customize it. + type = types.nullOr (types.submodule { + options = { + virtualHost = mkOption { + type = types.str; + default = "piwik.${config.networking.hostName}"; + example = "piwik.$\{config.networking.hostName\}"; + description = '' + Name of the nginx virtualhost to use and set up. + ''; + }; + enableSSL = mkOption { + type = types.bool; + default = true; + description = "Whether to enable https."; + }; + forceSSL = mkOption { + type = types.bool; + default = true; + description = "Whether to always redirect to https."; + }; + enableACME = mkOption { + type = types.bool; + default = true; + description = "Whether to ask Let's Encrypt to sign a certificate for this vhost."; + }; + }; + }); + default = null; + example = { virtualHost = "stats.$\{config.networking.hostName\}"; }; + description = '' + The options to use to configure an nginx virtualHost. + If null (the default), no nginx virtualHost will be configured. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + + users.extraUsers.${user} = { + isSystemUser = true; + createHome = true; + home = dataDir; + group = user; + }; + users.extraGroups.${user} = {}; + + systemd.services.piwik_setup_update = { + # everything needs to set up and up to date before piwik php files are executed + requiredBy = [ "${phpExecutionUnit}.service" ]; + before = [ "${phpExecutionUnit}.service" ]; + # the update part of the script can only work if the database is already up and running + requires = [ databaseService ]; + after = [ databaseService ]; + path = [ pkgs.piwik ]; + serviceConfig = { + Type = "oneshot"; + User = user; + # hide especially config.ini.php from other + UMask = "0007"; + Environment = "PIWIK_USER_PATH=${dataDir}"; + # chown + chmod in preStart needs root + PermissionsStartOnly = true; + }; + # correct ownership and permissions in case they're not correct anymore, + # e.g. after restoring from backup or moving from another system. + # Note that ${dataDir}/config/config.ini.php might contain the MySQL password. + preStart = '' + chown -R ${user}:${user} ${dataDir} + chmod -R ug+rwX,o-rwx ${dataDir} + ''; + script = '' + # Use User-Private Group scheme to protect piwik data, but allow administration / backup via piwik group + # Copy config folder + chmod g+s "${dataDir}" + cp -r "${pkgs.piwik}/config" "${dataDir}/" + chmod -R u+rwX,g+rwX,o-rwx "${dataDir}" + + # check whether user setup has already been done + if test -f "${dataDir}/config/config.ini.php"; then + # then execute possibly pending database upgrade + piwik-console core:update --yes + fi + ''; + }; + + systemd.services.${phpExecutionUnit} = { + # stop phpfpm on package upgrade, do database upgrade via piwik_setup_update, and then restart + restartTriggers = [ pkgs.piwik ]; + # stop config.ini.php from getting written with read permission for others + serviceConfig.UMask = "0007"; + }; + + services.phpfpm.poolConfigs = { + ${pool} = '' + listen = "${phpSocket}" + listen.owner = ${cfg.webServerUser} + listen.group = root + listen.mode = 0600 + user = ${user} + env[PIWIK_USER_PATH] = ${dataDir} + ${cfg.phpfpmProcessManagerConfig} + ''; + }; + + + services.nginx.virtualHosts = mkIf (cfg.nginx != null) { + # References: + # https://fralef.me/piwik-hardening-with-nginx-and-php-fpm.html + # https://github.com/perusio/piwik-nginx + ${cfg.nginx.virtualHost} = { + root = "${pkgs.piwik}/share"; + enableSSL = cfg.nginx.enableSSL; + enableACME = cfg.nginx.enableACME; + forceSSL = cfg.nginx.forceSSL; + + locations."/" = { + index = "index.php"; + }; + # allow index.php for webinterface + locations."= /index.php".extraConfig = '' + fastcgi_pass unix:${phpSocket}; + ''; + # allow piwik.php for tracking + locations."= /piwik.php".extraConfig = '' + fastcgi_pass unix:${phpSocket}; + ''; + # Any other attempt to access any php files is forbidden + locations."~* ^.+\.php$".extraConfig = '' + return 403; + ''; + # Disallow access to unneeded directories + # config and tmp are already removed + locations."~ ^/(?:core|lang|misc)/".extraConfig = '' + return 403; + ''; + # Disallow access to several helper files + locations."~* \.(?:bat|git|ini|sh|txt|tpl|xml|md)$".extraConfig = '' + return 403; + ''; + # No crawling of this site for bots that obey robots.txt - no useful information here. + locations."= /robots.txt".extraConfig = '' + return 200 "User-agent: *\nDisallow: /\n"; + ''; + # let browsers cache piwik.js + locations."= /piwik.js".extraConfig = '' + expires 1M; + ''; + }; + }; + }; + + meta = { + doc = ./piwik-doc.xml; + maintainers = with stdenv.lib.maintainers; [ florianjacob ]; + }; +} diff --git a/nixos/modules/services/web-servers/minio.nix b/nixos/modules/services/web-servers/minio.nix new file mode 100644 index 000000000000..1893edf3a776 --- /dev/null +++ b/nixos/modules/services/web-servers/minio.nix @@ -0,0 +1,69 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.minio; +in +{ + meta.maintainers = [ maintainers.bachp ]; + + options.services.minio = { + enable = mkEnableOption "Minio Object Storage"; + + listenAddress = mkOption { + default = ":9000"; + type = types.str; + description = "Listen on a specific IP address and port."; + }; + + dataDir = mkOption { + default = "/var/lib/minio/data"; + type = types.path; + description = "The data directory, for storing the objects."; + }; + + configDir = mkOption { + default = "/var/lib/minio/config"; + type = types.path; + description = "The config directory, for the access keys and other settings."; + }; + + package = mkOption { + default = pkgs.minio; + defaultText = "pkgs.minio"; + type = types.package; + description = "Minio package to use."; + }; + }; + + config = mkIf cfg.enable { + systemd.services.minio = { + description = "Minio Object Storage"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + preStart = '' + # Make sure directories exist with correct owner + mkdir -p ${cfg.configDir} + chown -R minio:minio ${cfg.configDir} + mkdir -p ${cfg.dataDir} + chown minio:minio ${cfg.dataDir} + ''; + serviceConfig = { + PermissionsStartOnly = true; + ExecStart = "${cfg.package}/bin/minio server --address ${cfg.listenAddress} --config-dir=${cfg.configDir} ${cfg.dataDir}"; + Type = "simple"; + User = "minio"; + Group = "minio"; + LimitNOFILE = 65536; + }; + }; + + users.extraUsers.minio = { + group = "minio"; + uid = config.ids.uids.minio; + }; + + users.extraGroups.minio.gid = config.ids.uids.minio; + }; +} diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix index 01e1659b30bb..01bab8fccadb 100644 --- a/nixos/modules/services/x11/xserver.nix +++ b/nixos/modules/services/x11/xserver.nix @@ -651,9 +651,13 @@ in system.extraDependencies = singleton (pkgs.runCommand "xkb-layouts-exist" { inherit (cfg) layout xkbDir; } '' - sed -n -e ':i /^! \(layout\|variant\) *$/ { - :l; n; /^!/bi; s/^ *\([^ ]\+\).*/\1/p; tl - }' "$xkbDir/rules/base.lst" | grep -qxF "$layout" && exec touch "$out" + if sed -n -e ':i /^! \(layout\|variant\) *$/ { + :l; n; /^!/bi; s/^ *\([^ ]\+\).*/\1/p; tl + }' "$xkbDir/rules/base.lst" | grep -qxF "$layout" + then + touch "$out" + exit 0 + fi cat >&2 <<-EOF diff --git a/nixos/release-combined.nix b/nixos/release-combined.nix index eca2d281342f..49ddd95887be 100644 --- a/nixos/release-combined.nix +++ b/nixos/release-combined.nix @@ -52,6 +52,7 @@ in rec { (all nixos.tests.firefox) (all nixos.tests.firewall) nixos.tests.gnome3.x86_64-linux # FIXME: i686-linux + (all nixos.tests.installer.zfsroot) (all nixos.tests.installer.lvm) (all nixos.tests.installer.luksroot) (all nixos.tests.installer.separateBoot) diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix index 85d31334d6bf..a6afe8a4b72a 100644 --- a/nixos/tests/installer.nix +++ b/nixos/tests/installer.nix @@ -171,6 +171,7 @@ let makeInstallerTest = name: { createPartitions, preBootCommands ? "", extraConfig ? "" + , extraInstallerConfig ? {} , bootLoader ? "grub" # either "grub" or "systemd-boot" , grubVersion ? 2, grubDevice ? "/dev/vda", grubIdentifier ? "uuid" , enableOCR ? false, meta ? {} @@ -192,6 +193,7 @@ let { imports = [ ../modules/profiles/installation-device.nix ../modules/profiles/base.nix + extraInstallerConfig ]; virtualisation.diskSize = 8 * 1024; @@ -332,6 +334,43 @@ in { ''; }; + # zfs on / with swap + zfsroot = makeInstallerTest "zfs-root" + { + extraInstallerConfig = { + boot.supportedFilesystems = [ "zfs" ]; + }; + + extraConfig = '' + boot.supportedFilesystems = [ "zfs" ]; + + # Using by-uuid overrides the default of by-id, and is unique + # to the qemu disks, as they don't produce by-id paths for + # some reason. + boot.zfs.devNodes = "/dev/disk/by-uuid/"; + networking.hostId = "00000000"; + ''; + + createPartitions = + '' + $machine->succeed( + "parted /dev/vda mklabel msdos", + "parted /dev/vda -- mkpart primary linux-swap 1M 1024M", + "parted /dev/vda -- mkpart primary 1024M -1s", + "udevadm settle", + + "mkswap /dev/vda1 -L swap", + "swapon -L swap", + + "zpool create rpool /dev/vda2", + "zfs create -o mountpoint=legacy rpool/root", + "mount -t zfs rpool/root /mnt", + + "udevadm settle" + ); + ''; + }; + # Create two physical LVM partitions combined into one volume group # that contains the logical swap and root partitions. lvm = makeInstallerTest "lvm" diff --git a/nixos/tests/minio.nix b/nixos/tests/minio.nix new file mode 100644 index 000000000000..462a3bc4768c --- /dev/null +++ b/nixos/tests/minio.nix @@ -0,0 +1,19 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "minio"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ bachp ]; + }; + + machine = { config, pkgs, ... }: { + services.minio.enable = true; + }; + + testScript = + '' + startAll; + $machine->waitForUnit("minio.service"); + $machine->waitForOpenPort(9000); + $machine->succeed("curl --fail http://localhost:9000/minio/index.html"); + $machine->shutdown; + ''; +}) |