{ config, lib, libC, pkgs, ... }: let cfg = config.services.mediawiki; in { assertions = [ { assertion = lib.versions.majorMinor pkgs.mediawiki.version != 1.40; # https://www.mediawiki.org/wiki/Version_lifecycle message = "Please keep mediawiki on LTS versions which is required by the LDAP extension"; } ]; c3d2 = { deployment.server = "server10"; hq.sendmail = true; }; microvm.mem = 1024; networking = { firewall.allowedTCPPorts = [ 80 443 ]; # httpd, not nginx :( hostName = "mediawiki"; }; services = { backup.paths = [ "/var/lib/mediawiki/uploads/" ]; logrotate.checkConfig = false; mediawiki = { enable = true; database = { type = "postgres"; socket = "/run/postgresql"; }; # see https://extdist.wmflabs.org/dist/extensions/ for list of extensions # save them on https://web.archive.org/save and copy the final URL below extensions = { Lockdown = pkgs.fetchzip { url = "https://web.archive.org/web/20230710141042/https://extdist.wmflabs.org/dist/extensions/Lockdown-REL1_40-7d900ed.tar.gz"; sha256 = "sha256-TgoL9IcwY4EBNUsoVBqpUehVO7TEDT22FoH7Ep4dMxw="; }; # TODO: replace with https://www.mediawiki.org/wiki/Extension:DynamicPageList3 intersection = pkgs.fetchzip { url = "https://web.archive.org/web/20230710142223/https://extdist.wmflabs.org/dist/extensions/intersection-REL1_40-f3c1559.tar.gz"; sha256 = "sha256-/Ap56EOfsAlx/gkN8Y+NdU28yuZsZmrrBMmuNoi88/A="; }; # requires PluggableAuth LDAPAuthentication2 = let src = pkgs.fetchzip { url = "https://web.archive.org/web/20230710142325/https://extdist.wmflabs.org/dist/extensions/LDAPAuthentication2-REL1_40-2864ae9.tar.gz"; sha256 = "sha256-LWXpmgzUpgEaPe/4cwF2cmJxPkW8ywT7gRAlB58mDfY="; }; in # TODO: remove with next release pkgs.runCommand "LDAPAuthentication2" { } '' mkdir $out cp -r ${src}/* $out sed 's/"PluggableAuth": "6.*"/"PluggableAuth": "*"/g' -i $out/extension.json ''; LDAPProvider = pkgs.fetchzip { url = "https://web.archive.org/web/20230710141035/https://extdist.wmflabs.org/dist/extensions/LDAPProvider-REL1_40-99edc23.tar.gz"; sha256 = "sha256-DYq5CCm//rc6Mei9K6S2Ue+hzz6PYHnwpbJouFS5j+o="; }; PluggableAuth = pkgs.fetchzip { url = "https://web.archive.org/web/20230710142618/https://extdist.wmflabs.org/dist/extensions/PluggableAuth-REL1_40-519c6d2.tar.gz"; sha256 = "sha256-N1+OV1UdzvU4iXhaS/+fuEoAXqrkVyyEPDirk0vrT8A="; }; }; name = "C3D2"; nginx.hostName = "wiki.c3d2.de"; package = pkgs.php81.buildComposerProject { pname = "mediawiki-pre-full"; inherit (pkgs.mediawiki) version; src = pkgs.applyPatches { inherit (pkgs.mediawiki) src; postPatch = '' cp ${./composer.local.json} composer.local.json cp ${./composer.lock} composer.lock ''; }; composerNoPlugins = false; vendorHash = "sha256-oa39DG55dD7cRED0MWFHJCg+HF8lhf4mJw8/vS7J9gI="; postInstall = '' mv $out/share/{php/mediawiki-pre-full,mediawiki}/ ''; }; #skins = { # Vector = "${config.services.mediawiki.package}/share/mediawiki/skins/Vector"; # Hector = "${config.services.mediawiki.package}/share/mediawiki/skins/Hector"; #}; # initial admin user password passwordFile = config.sops.secrets."mediawiki/adminPassword".path; uploadsDir = "/var/lib/mediawiki/uploads"; webserver = "nginx"; extraConfig = /* php */ '' $wgAllowUserCss = true; $wgDBmwschema = "mediawiki"; $wgEnableAPI = true; $wgEnableMWSuggest = true; $wgShowExceptionDetails = true; $wgUseAjax = true; $wgFavicon = "https://c3d2.de/favicon.ico"; $wgLogos = [ '1x' => "https://www.c3d2.de/images/ck.png", '1.5x' => "https://www.c3d2.de/images/ck.png", '2x' => "https://www.c3d2.de/images/ck.png", 'icon' => "https://www.c3d2.de/images/ck.png", ]; $wgEmergencyContact = "wiki@c3d2.de"; $wgPasswordSender = "wiki@c3d2.de"; $wgLanguageCode = "de"; $wgGroupPermissions['*']['edit'] = false; $wgGroupPermissions['user']['edit'] = true; $wgGroupPermissions['sysop']['userrights'] = true; define("NS_INTERN", 100); define("NS_INTERN_TALK", 101); $wgExtraNamespaces[NS_INTERN] = "Intern"; $wgExtraNamespaces[NS_INTERN_TALK] = "Intern_Diskussion"; $wgGroupPermissions['intern']['move'] = true; $wgGroupPermissions['intern']['move-subpages'] = true; $wgGroupPermissions['intern']['move-rootuserpages'] = true; // can move root userpages $wgGroupPermissions['intern']['read'] = true; $wgGroupPermissions['intern']['edit'] = true; $wgGroupPermissions['intern']['createpage'] = true; $wgGroupPermissions['intern']['createtalk'] = true; $wgGroupPermissions['intern']['writeapi'] = true; $wgGroupPermissions['intern']['upload'] = true; $wgGroupPermissions['intern']['reupload'] = true; $wgGroupPermissions['intern']['reupload-shared'] = true; $wgGroupPermissions['intern']['minoredit'] = true; $wgGroupPermissions['intern']['purge'] = true; // can use ?action=purge without clicking "ok" $wgGroupPermissions['intern']['sendemail'] = true; $wgNamespacePermissionLockdown[NS_INTERN]['*'] = array('intern'); $wgNamespacePermissionLockdown[NS_INTERN_TALK]['*'] = array('intern'); define("NS_I4R", 102); define("NS_I4R_TALK", 103); $wgExtraNamespaces[NS_I4R] = "IT4Refugees"; $wgExtraNamespaces[NS_I4R_TALK] = "IT4Refugees_Diskussion"; $wgGroupPermissions['i4r']['move'] = true; $wgGroupPermissions['i4r']['move-subpages'] = true; $wgGroupPermissions['i4r']['move-rootuserpages'] = true; // can move root userpages $wgGroupPermissions['i4r']['read'] = true; $wgGroupPermissions['i4r']['edit'] = true; $wgGroupPermissions['i4r']['createpage'] = true; $wgGroupPermissions['i4r']['createtalk'] = true; $wgGroupPermissions['i4r']['writeapi'] = true; $wgGroupPermissions['i4r']['upload'] = true; $wgGroupPermissions['i4r']['reupload'] = true; $wgGroupPermissions['i4r']['reupload-shared'] = true; $wgGroupPermissions['i4r']['minoredit'] = true; $wgGroupPermissions['i4r']['purge'] = true; // can use ?action=purge without clicking "ok" $wgGroupPermissions['i4r']['sendemail'] = true; $wgNamespacePermissionLockdown[NS_I4R]['*'] = array('i4r'); $wgNamespacePermissionLockdown[NS_I4R_TALK]['*'] = array('i4r'); $wgGroupPermissions['sysop']['deletelogentry'] = true; $wgGroupPermissions['sysop']['deleterevision'] = true; wfLoadExtension('ConfirmEdit/QuestyCaptcha'); $wgCaptchaClass = 'QuestyCaptcha'; $wgCaptchaQuestions[] = array('question' => 'How is C3D2 logo in ascii?', 'answer' => '<<>'); wfLoadExtension('Interwiki'); $wgGroupPermissions['sysop']['interwiki'] = true; wfLoadExtension('Cite'); wfLoadExtension('CiteThisPage'); wfLoadExtension('ConfirmEdit'); wfLoadExtension('ParserFunctions'); wfLoadExtension('SyntaxHighlight_GeSHi'); wfLoadExtension('WikiEditor'); // TODO: what about $wgUpgradeKey ? // TODO: does this even work? // https://www.mediawiki.org/wiki/Extension:Scribunto#Requirements mentions quite some extra steps which we didn't do wfLoadExtension('Scribunto'); $wgScribuntoDefaultEngine = 'luastandalone'; # LDAP $LDAPProviderDomainConfigs = "${config.sops.secrets."mediawiki/ldapprovider".path}"; $wgPluggableAuth_EnableLocalLogin = true; # SemanticMediaWiki wfLoadExtension('SemanticMediaWiki'); # TODO: expose https://github.com/NixOS/nixpkgs/blob/nixos-unstable/nixos/modules/services/web-apps/mediawiki.nix#L19 $smwgConfigFileDir = "/var/lib/mediawiki"; # default was 100 which is already occupied, using 200 to 215 # https://www.semantic-mediawiki.org/wiki/Help:$smwgNamespaceIndex $smwgNamespaceIndex = 200; enableSemantics('${config.services.mediawiki.nginx.hostName}'); $smwgURITypeSchemeList = array_merge( $smwgURITypeSchemeList, [ 'xmpp', 'mumble', 'ssh' ] ); ''; }; nginx = { enable = true; commonHttpConfig = '' # for some reason nginx adds a port for the 301 redirect from / to /wiki/ port_in_redirect off; ''; virtualHosts."${config.services.mediawiki.nginx.hostName}" = { enableACME = true; forceSSL = true; listen = libC.defaultListen; locations."/".extraConfig = lib.mkForce '' return 307 /wiki$request_uri; ''; }; }; phpfpm.phpPackage = pkgs.php.buildEnv { extensions = { all, enabled }: enabled ++ (with all; [ apcu ]); }; postgresql = { enable = true; ensureDatabases = [ cfg.database.name ]; ensureUsers = [{ name = cfg.database.user; ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; }; }]; package = pkgs.postgresql_16; upgrade.stopServices = [ "phpfpm-mediawiki" ]; }; }; sops = { age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; defaultSopsFile = ./secrets.yaml; secrets = { "mediawiki/adminPassword".owner = config.systemd.services.mediawiki-init.serviceConfig.User; "mediawiki/ldapprovider".owner = config.systemd.services.mediawiki-init.serviceConfig.User; "mediawiki/secretKey" = { owner = config.systemd.services.mediawiki-init.serviceConfig.User; path = "/var/lib/mediawiki/secret.key"; }; "mediawiki/upgradeKey".owner = config.systemd.services.mediawiki-init.serviceConfig.User; }; }; system.stateVersion = "22.05"; systemd.services.mediawiki-init = { after = [ "postgresql.service" ]; requires = [ "postgresql.service" ]; }; }