From 55844c8380b2f86bd23458a72abe473775a5136c Mon Sep 17 00:00:00 2001 From: Florian Jacob Date: Sun, 23 Apr 2017 15:51:21 +0200 Subject: [PATCH] piwik & piwik service: init at 3.0.4 --- lib/maintainers.nix | 1 + nixos/modules/module-list.nix | 1 + nixos/modules/services/web-apps/piwik-doc.xml | 97 ++++++++ nixos/modules/services/web-apps/piwik.nix | 219 ++++++++++++++++++ pkgs/servers/web-apps/piwik/bootstrap.php | 6 + pkgs/servers/web-apps/piwik/default.nix | 60 +++++ pkgs/top-level/all-packages.nix | 2 + 7 files changed, 386 insertions(+) create mode 100644 nixos/modules/services/web-apps/piwik-doc.xml create mode 100644 nixos/modules/services/web-apps/piwik.nix create mode 100644 pkgs/servers/web-apps/piwik/bootstrap.php create mode 100644 pkgs/servers/web-apps/piwik/default.nix diff --git a/lib/maintainers.nix b/lib/maintainers.nix index d4f9d1c31a8e..55435189d26b 100644 --- a/lib/maintainers.nix +++ b/lib/maintainers.nix @@ -181,6 +181,7 @@ fadenb = "Tristan Helmich "; fare = "Francois-Rene Rideau "; falsifian = "James Cook "; + florianjacob = "Florian Jacob "; flosse = "Markus Kohlhase "; fluffynukeit = "Daniel Austin "; fmthoma = "Franz Thoma "; diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 7f94d6569e97..7d5d922dd051 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -575,6 +575,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 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 @@ + + + Piwik + + Piwik is a real-time web analytics application. + This module configures php-fpm as backend for piwik, optionally configuring an nginx vhost as well. + + + + An automatic setup is not suported by piwik, so you need to configure piwik itself in the browser-based piwik setup. + + + +
+ Database Setup + + + 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: + + 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'; + + Then fill in piwik 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 piwik unix + user to authenticate as piwik database (without needing a password), but no other users. + For more information on passwordless login, see + . + + + + Of course, you can use password based authentication as well, e.g. when the database is not on the same host. + +
+ + +
+ Backup + + You only need to take backups of your MySQL database and the + /var/lib/piwik/config/config.ini.php file. + Use a user in the piwik group or root to access the file. + For more information, see . + +
+ + +
+ Issues + + + + Piwik's file integrity check will warn you. + This is due to the patches necessary for NixOS, you can safely ignore this. + + + + + + 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. + + + + + + Sending mail from piwik, e.g. for the password reset function, might not work out of the box: + There's a problem with using sendmail from php-fpm that is + being investigated at . + 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 General Settings > Mail Server Settings instead. + + + +
+ + +
+ Using other Web Servers than nginx + + + You can use other web servers by forwarding calls for index.php and + piwik.php to the /run/phpfpm-piwik.sock fastcgi unix socket. + You can use the nginx configuration in the module code as a reference to what else should be configured. + +
+
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/pkgs/servers/web-apps/piwik/bootstrap.php b/pkgs/servers/web-apps/piwik/bootstrap.php new file mode 100644 index 000000000000..a8163f33e5c0 --- /dev/null +++ b/pkgs/servers/web-apps/piwik/bootstrap.php @@ -0,0 +1,6 @@ + '127.0.0.1'," "=> 'localhost'," + cp ${./bootstrap.php} bootstrap.php + ''; + + # TODO: Move more unnecessary files from share/, especially using PIWIK_INCLUDE_PATH. + # See https://forum.piwik.org/t/bootstrap-php/5926/10 and + # https://github.com/piwik/piwik/issues/11654#issuecomment-297730843 + installPhase = '' + runHook preInstall + + # copy evertything to share/, used as webroot folder, and then remove what's known to be not needed + mkdir -p $out/share + cp -ra * $out/share/ + # tmp/ is created by piwik in PIWIK_USER_PATH + rmdir $out/share/tmp + # config/ needs to be copied to PIWIK_USER_PATH anyway + mv $out/share/config $out/ + + makeWrapper ${php}/bin/php $out/bin/piwik-console \ + --add-flags "$out/share/console" + + runHook postInstall + ''; + + meta = with stdenv.lib; { + description = "A real-time web analytics application"; + license = licenses.gpl3Plus; + homepage = https://piwik.org/; + platforms = platforms.all; + maintainers = [ maintainers.florianjacob ]; + }; +} diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index c87ab0a1c4ea..f23ef8971b3c 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -11407,6 +11407,8 @@ with pkgs; shaarli-material = callPackage ../servers/web-apps/shaarli/material-theme.nix { }; + piwik = callPackage ../servers/web-apps/piwik { }; + axis2 = callPackage ../servers/http/tomcat/axis2 { }; unifi = callPackage ../servers/unifi { };