From 455d33f5143c1127175b2767110c3b1a152a0461 Mon Sep 17 00:00:00 2001 From: Aaron Andersen Date: Wed, 5 Jun 2019 21:19:11 -0400 Subject: nixos/mediawiki: init service to replace httpd subservice --- nixos/modules/services/web-apps/mediawiki.nix | 473 +++++++++++++++++++++ .../web-servers/apache-httpd/mediawiki.nix | 349 --------------- 2 files changed, 473 insertions(+), 349 deletions(-) create mode 100644 nixos/modules/services/web-apps/mediawiki.nix delete mode 100644 nixos/modules/services/web-servers/apache-httpd/mediawiki.nix (limited to 'nixos/modules/services') diff --git a/nixos/modules/services/web-apps/mediawiki.nix b/nixos/modules/services/web-apps/mediawiki.nix new file mode 100644 index 000000000000..5bd5977e592b --- /dev/null +++ b/nixos/modules/services/web-apps/mediawiki.nix @@ -0,0 +1,473 @@ +{ config, pkgs, lib, ... }: + +let + + inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption; + inherit (lib) concatStringsSep literalExample mapAttrsToList optional optionals optionalString types; + + cfg = config.services.mediawiki; + fpm = config.services.phpfpm.pools.mediawiki; + user = "mediawiki"; + group = config.services.httpd.group; + cacheDir = "/var/cache/mediawiki"; + stateDir = "/var/lib/mediawiki"; + + pkg = pkgs.stdenv.mkDerivation rec { + pname = "mediawiki-full"; + version = src.version; + src = cfg.package; + + installPhase = '' + mkdir -p $out + cp -r * $out/ + + rm -rf $out/share/mediawiki/skins/* + rm -rf $out/share/mediawiki/extensions/* + + ${concatStringsSep "\n" (mapAttrsToList (k: v: '' + ln -s ${v} $out/share/mediawiki/skins/${k} + '') cfg.skins)} + + ${concatStringsSep "\n" (mapAttrsToList (k: v: '' + ln -s ${v} $out/share/mediawiki/extensions/${k} + '') cfg.extensions)} + ''; + }; + + mediawikiScripts = pkgs.runCommand "mediawiki-scripts" { + buildInputs = [ pkgs.makeWrapper ]; + preferLocalBuild = true; + } '' + mkdir -p $out/bin + for i in changePassword.php createAndPromote.php userOptions.php edit.php nukePage.php update.php; do + makeWrapper ${pkgs.php}/bin/php $out/bin/mediawiki-$(basename $i .php) \ + --set MEDIAWIKI_CONFIG ${mediawikiConfig} \ + --add-flags ${pkg}/share/mediawiki/maintenance/$i + done + ''; + + mediawikiConfig = pkgs.writeText "LocalSettings.php" '' + database.user. + ''; + }; + + tablePrefix = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + If you only have access to a single database and wish to install more than + one version of MediaWiki, or have other applications that also use the + database, you can give the table names a unique prefix to stop any naming + conflicts or confusion. + See . + ''; + }; + + socket = mkOption { + type = types.nullOr types.path; + default = if cfg.database.createLocally then "/run/mysqld/mysqld.sock" else null; + defaultText = "/run/mysqld/mysqld.sock"; + description = "Path to the unix socket file to use for authentication."; + }; + + createLocally = mkOption { + type = types.bool; + default = cfg.database.type == "mysql"; + defaultText = "true"; + description = '' + Create the database and database user locally. + This currently only applies if database type "mysql" is selected. + ''; + }; + }; + + virtualHost = mkOption { + type = types.submodule ({ + options = import ../web-servers/apache-httpd/per-server-options.nix { + inherit lib; + forMainServer = false; + }; + }); + example = literalExample '' + { + hostName = "mediawiki.example.org"; + enableSSL = true; + adminAddr = "webmaster@example.org"; + sslServerCert = "/var/lib/acme/mediawiki.example.org/full.pem"; + sslServerKey = "/var/lib/acme/mediawiki.example.org/key.pem"; + } + ''; + description = '' + Apache configuration can be done by adapting . + See 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 MediaWiki's PHP pool. See the documentation on php-fpm.conf + for details on configuration directives. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + description = '' + Any additional text to be appended to MediaWiki's + LocalSettings.php configuration file. For configuration + settings, see . + ''; + default = ""; + example = '' + $wgEnableEmail = false; + ''; + }; + + }; + }; + + # implementation + config = mkIf cfg.enable { + + assertions = [ + { assertion = cfg.database.createLocally -> cfg.database.type == "mysql"; + message = "services.mediawiki.createLocally is currently only supported for database type 'mysql'"; + } + { assertion = cfg.database.createLocally -> cfg.database.user == user; + message = "services.mediawiki.database.user must be set to ${user} if services.mediawiki.database.createLocally is set true"; + } + { assertion = cfg.database.createLocally -> cfg.database.socket != null; + message = "services.mediawiki.database.socket must be set if services.mediawiki.database.createLocally is set to true"; + } + { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null; + message = "a password cannot be specified if services.mediawiki.database.createLocally is set to true"; + } + ]; + + services.mediawiki.skins = { + MonoBook = "${cfg.package}/share/mediawiki/skins/MonoBook"; + Timeless = "${cfg.package}/share/mediawiki/skins/Timeless"; + Vector = "${cfg.package}/share/mediawiki/skins/Vector"; + }; + + services.mysql = mkIf cfg.database.createLocally { + enable = true; + package = mkDefault pkgs.mariadb; + ensureDatabases = [ cfg.database.name ]; + ensureUsers = [ + { name = cfg.database.user; + ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; }; + } + ]; + }; + + services.phpfpm.pools.mediawiki = { + listen = "/run/phpfpm/mediawiki.sock"; + extraConfig = '' + listen.owner = ${config.services.httpd.user} + listen.group = ${config.services.httpd.group} + user = ${user} + group = ${group} + + env[MEDIAWIKI_CONFIG] = ${mediawikiConfig} + + ${cfg.poolConfig} + ''; + }; + + services.httpd = { + enable = true; + adminAddr = mkDefault cfg.virtualHost.adminAddr; + extraModules = [ "proxy_fcgi" ]; + virtualHosts = [ (mkMerge [ + cfg.virtualHost { + documentRoot = mkForce "${pkg}/share/mediawiki"; + extraConfig = '' + + + + SetHandler "proxy:unix:${fpm.listen}|fcgi://localhost/" + + + + Require all granted + DirectoryIndex index.php + AllowOverride All + + '' + optionalString (cfg.uploadsDir != null) '' + Alias "/images" "${cfg.uploadsDir}" + + Require all granted + + ''; + } + ]) ]; + }; + + systemd.tmpfiles.rules = [ + "d '${stateDir}' 0750 ${user} ${group} - -" + "d '${cacheDir}' 0750 ${user} ${group} - -" + ] ++ optionals (cfg.uploadsDir != null) [ + "d '${cfg.uploadsDir}' 0750 ${user} ${group} - -" + "Z '${cfg.uploadsDir}' 0750 ${user} ${group} - -" + ]; + + systemd.services.mediawiki-init = { + wantedBy = [ "multi-user.target" ]; + before = [ "phpfpm-mediawiki.service" ]; + after = optional cfg.database.createLocally "mysql.service"; + script = '' + if ! test -e "${stateDir}/secret.key"; then + tr -dc A-Za-z0-9 /dev/null | head -c 64 > ${stateDir}/secret.key + fi + + echo "exit( wfGetDB( DB_MASTER )->tableExists( 'user' ) ? 1 : 0 );" | \ + ${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/eval.php --conf ${mediawikiConfig} && \ + ${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/install.php \ + --confpath /tmp \ + --scriptpath / \ + --dbserver ${cfg.database.host}${optionalString (cfg.database.socket != null) ":${cfg.database.socket}"} \ + --dbport ${toString cfg.database.port} \ + --dbname ${cfg.database.name} \ + ${optionalString (cfg.database.tablePrefix != null) "--dbprefix ${cfg.database.tablePrefix}"} \ + --dbuser ${cfg.database.user} \ + ${optionalString (cfg.database.passwordFile != null) "--dbpassfile ${cfg.database.passwordFile}"} \ + --passfile ${cfg.passwordFile} \ + ${cfg.name} \ + admin + + ${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/update.php --conf ${mediawikiConfig} --quick + ''; + + serviceConfig = { + Type = "oneshot"; + User = user; + Group = group; + PrivateTmp = true; + }; + }; + + systemd.services.httpd.after = optional (cfg.database.createLocally && cfg.database.type == "mysql") "mysql.service"; + + users.users.${user}.group = group; + + environment.systemPackages = [ mediawikiScripts ]; + }; +} diff --git a/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix b/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix deleted file mode 100644 index 6234478014ce..000000000000 --- a/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix +++ /dev/null @@ -1,349 +0,0 @@ -{ config, lib, pkgs, serverInfo, php, ... }: - -with lib; - -let - - httpd = serverInfo.serverConfig.package; - - version24 = !versionOlder httpd.version "2.4"; - - allGranted = if version24 then '' - Require all granted - '' else '' - Order allow,deny - Allow from all - ''; - - mediawikiConfig = pkgs.writeText "LocalSettings.php" - '' - - ''; - - # Unpack Mediawiki and put the config file in its root directory. - mediawikiRoot = pkgs.stdenv.mkDerivation rec { - name= "mediawiki-1.31.1"; - - src = pkgs.fetchurl { - url = "https://releases.wikimedia.org/mediawiki/1.31/${name}.tar.gz"; - sha256 = "13x48clij21cmysjkpnx68vggchrdasqp7b290j87xlfgjhdhnnf"; - }; - - skins = config.skins; - extensions = config.extensions; - - buildPhase = - '' - for skin in $skins; do - cp -prvd $skin/* skins/ - done - for extension in $extensions; do - cp -prvd $extension/* extensions/ - done - ''; # */ - - installPhase = - '' - mkdir -p $out - cp -r * $out - cp ${mediawikiConfig} $out/LocalSettings.php - sed -i \ - -e 's|/bin/bash|${pkgs.bash}/bin/bash|g' \ - -e 's|/usr/bin/timeout|${pkgs.coreutils}/bin/timeout|g' \ - $out/includes/shell/limit.sh \ - $out/includes/GlobalFunctions.php - ''; - }; - - mediawikiScripts = pkgs.runCommand "mediawiki-${config.id}-scripts" { - buildInputs = [ pkgs.makeWrapper ]; - preferLocalBuild = true; - } '' - mkdir -p $out/bin - for i in changePassword.php createAndPromote.php userOptions.php edit.php nukePage.php update.php; do - makeWrapper ${php}/bin/php $out/bin/mediawiki-${config.id}-$(basename $i .php) \ - --add-flags ${mediawikiRoot}/maintenance/$i - done - ''; - -in - -{ - - extraConfig = - '' - ${optionalString config.enableUploads '' - Alias ${config.urlPrefix}/images ${config.uploadDir} - - - ${allGranted} - Options -Indexes - - ''} - - ${if config.urlPrefix != "" then "Alias ${config.urlPrefix} ${mediawikiRoot}" else '' - RewriteEngine On - RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f - RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d - ${concatMapStringsSep "\n" (u: "RewriteCond %{REQUEST_URI} !^${u.urlPath}") serverInfo.vhostConfig.servedDirs} - ${concatMapStringsSep "\n" (u: "RewriteCond %{REQUEST_URI} !^${u.urlPath}") serverInfo.vhostConfig.servedFiles} - RewriteRule ${if config.enableUploads - then "!^/images" - else "^.*\$" - } %{DOCUMENT_ROOT}/${if config.articleUrlPrefix == "" - then "" - else "${config.articleUrlPrefix}/" - }index.php [L] - ''} - - - ${allGranted} - DirectoryIndex index.php - - - ${optionalString (config.articleUrlPrefix != "") '' - Alias ${config.articleUrlPrefix} ${mediawikiRoot}/index.php - ''} - ''; - - documentRoot = if config.urlPrefix == "" then mediawikiRoot else null; - - enablePHP = true; - - options = { - - id = mkOption { - default = "main"; - description = '' - A unique identifier necessary to keep multiple MediaWiki server - instances on the same machine apart. This is used to - disambiguate the administrative scripts, which get names like - mediawiki-$id-change-password. - ''; - }; - - dbType = mkOption { - default = "postgres"; - example = "mysql"; - description = "Database type."; - }; - - dbName = mkOption { - default = "mediawiki"; - description = "Name of the database that holds the MediaWiki data."; - }; - - dbServer = mkOption { - default = ""; # use a Unix domain socket - example = "10.0.2.2"; - description = '' - The location of the database server. Leave empty to use a - database server running on the same machine through a Unix - domain socket. - ''; - }; - - dbUser = mkOption { - default = "mediawiki"; - description = "The user name for accessing the database."; - }; - - dbPassword = mkOption { - default = ""; - example = "foobar"; - description = '' - The password of the database user. Warning: this is stored in - cleartext in the Nix store! - ''; - }; - - emergencyContact = mkOption { - default = serverInfo.serverConfig.adminAddr; - example = "admin@example.com"; - description = '' - Emergency contact e-mail address. Defaults to the Apache - admin address. - ''; - }; - - passwordSender = mkOption { - default = serverInfo.serverConfig.adminAddr; - example = "password@example.com"; - description = '' - E-mail address from which password confirmations originate. - Defaults to the Apache admin address. - ''; - }; - - siteName = mkOption { - default = "MediaWiki"; - example = "Foobar Wiki"; - description = "Name of the wiki"; - }; - - logo = mkOption { - default = ""; - example = "/images/logo.png"; - description = "The URL of the site's logo (which should be a 135x135px image)."; - }; - - urlPrefix = mkOption { - default = "/w"; - description = '' - The URL prefix under which the Mediawiki service appears. - ''; - }; - - articleUrlPrefix = mkOption { - default = "/wiki"; - example = ""; - description = '' - The URL prefix under which article pages appear, - e.g. http://server/wiki/Page. Leave empty to use the main URL - prefix, e.g. http://server/w/index.php?title=Page. - ''; - }; - - enableUploads = mkOption { - default = false; - description = "Whether to enable file uploads."; - }; - - uploadDir = mkOption { - default = throw "You must specify `uploadDir'."; - example = "/data/mediawiki-upload"; - description = "The directory that stores uploaded files."; - }; - - defaultSkin = mkOption { - default = ""; - example = "nostalgia"; - description = "Set this value to change the default skin used by MediaWiki."; - }; - - skins = mkOption { - default = []; - type = types.listOf types.path; - description = - '' - List of paths whose content is copied to the ‘skins’ - subdirectory of the MediaWiki installation. - ''; - }; - - extensions = mkOption { - default = []; - type = types.listOf types.path; - description = - '' - List of paths whose content is copied to the 'extensions' - subdirectory of the MediaWiki installation. - ''; - }; - - extraConfig = mkOption { - type = types.lines; - default = ""; - example = - '' - $wgEnableEmail = false; - ''; - description = '' - Any additional text to be appended to MediaWiki's - configuration file. This is a PHP script. For configuration - settings, see . - ''; - }; - - }; - - extraPath = [ mediawikiScripts ]; - - # !!! Need to specify that Apache has a dependency on PostgreSQL! - - startupScript = pkgs.writeScript "mediawiki_startup.sh" - # Initialise the database automagically if we're using a Postgres - # server on localhost. - (optionalString (config.dbType == "postgres" && config.dbServer == "") '' - if ! ${pkgs.postgresql}/bin/psql -l | grep -q ' ${config.dbName} ' ; then - ${pkgs.postgresql}/bin/createuser --no-superuser --no-createdb --no-createrole "${config.dbUser}" || true - ${pkgs.postgresql}/bin/createdb "${config.dbName}" -O "${config.dbUser}" - ( echo 'CREATE LANGUAGE plpgsql;' - cat ${mediawikiRoot}/maintenance/postgres/tables.sql - echo 'CREATE TEXT SEARCH CONFIGURATION public.default ( COPY = pg_catalog.english );' - echo COMMIT - ) | ${pkgs.postgresql}/bin/psql -U "${config.dbUser}" "${config.dbName}" - fi - ${php}/bin/php ${mediawikiRoot}/maintenance/update.php - ''); - - robotsEntries = optionalString (config.articleUrlPrefix != "") - '' - User-agent: * - Disallow: ${config.urlPrefix}/ - Disallow: ${config.articleUrlPrefix}/Special:Search - Disallow: ${config.articleUrlPrefix}/Special:Random - ''; - -} -- cgit 1.4.1