diff options
author | Marek Mahut <marek.mahut@gmail.com> | 2019-11-03 21:36:12 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-11-03 21:36:12 +0100 |
commit | 794c9197656936318dd92a1d2c7728675e4b2e46 (patch) | |
tree | 960f4c13556e6ecfb2c453eb05ce98951aedbc6e /nixos | |
parent | d8cf78466e56b798cdecd2dbbacdfe00bd7e4ef8 (diff) | |
parent | ad034104cc97f7d7c4b1f20627f77d9d61f5b182 (diff) | |
download | nixpkgs-794c9197656936318dd92a1d2c7728675e4b2e46.tar nixpkgs-794c9197656936318dd92a1d2c7728675e4b2e46.tar.gz nixpkgs-794c9197656936318dd92a1d2c7728675e4b2e46.tar.bz2 nixpkgs-794c9197656936318dd92a1d2c7728675e4b2e46.tar.lz nixpkgs-794c9197656936318dd92a1d2c7728675e4b2e46.tar.xz nixpkgs-794c9197656936318dd92a1d2c7728675e4b2e46.tar.zst nixpkgs-794c9197656936318dd92a1d2c7728675e4b2e46.zip |
Merge pull request #68327 from mmilata/moin
nixos/moinmoin: init module
Diffstat (limited to 'nixos')
-rw-r--r-- | nixos/modules/module-list.nix | 1 | ||||
-rw-r--r-- | nixos/modules/services/web-apps/moinmoin.nix | 303 | ||||
-rw-r--r-- | nixos/tests/all-tests.nix | 1 | ||||
-rw-r--r-- | nixos/tests/moinmoin.nix | 24 |
4 files changed, 329 insertions, 0 deletions
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 88c3a6d73ac..1dcbf71dfaf 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -813,6 +813,7 @@ ./services/web-apps/nexus.nix ./services/web-apps/pgpkeyserver-lite.nix ./services/web-apps/matomo.nix + ./services/web-apps/moinmoin.nix ./services/web-apps/restya-board.nix ./services/web-apps/tt-rss.nix ./services/web-apps/selfoss.nix diff --git a/nixos/modules/services/web-apps/moinmoin.nix b/nixos/modules/services/web-apps/moinmoin.nix new file mode 100644 index 00000000000..0fee64be0bb --- /dev/null +++ b/nixos/modules/services/web-apps/moinmoin.nix @@ -0,0 +1,303 @@ +{ config, lib, pkgs, ... }: +with lib; + +let + cfg = config.services.moinmoin; + python = pkgs.python27; + pkg = python.pkgs.moinmoin; + dataDir = "/var/lib/moin"; + usingGunicorn = cfg.webServer == "nginx-gunicorn" || cfg.webServer == "gunicorn"; + usingNginx = cfg.webServer == "nginx-gunicorn"; + user = "moin"; + group = "moin"; + + uLit = s: ''u"${s}"''; + indentLines = n: str: concatMapStrings (line: "${fixedWidthString n " " " "}${line}\n") (splitString "\n" str); + + moinCliWrapper = wikiIdent: pkgs.writeShellScriptBin "moin-${wikiIdent}" '' + ${pkgs.su}/bin/su -s ${pkgs.runtimeShell} -c "${pkg}/bin/moin --config-dir=/var/lib/moin/${wikiIdent}/config $*" ${user} + ''; + + wikiConfig = wikiIdent: w: '' + # -*- coding: utf-8 -*- + + from MoinMoin.config import multiconfig, url_prefix_static + + class Config(multiconfig.DefaultConfig): + ${optionalString (w.webLocation != "/") '' + url_prefix_static = '${w.webLocation}' + url_prefix_static + ''} + + sitename = u'${w.siteName}' + page_front_page = u'${w.frontPage}' + + data_dir = '${dataDir}/${wikiIdent}/data' + data_underlay_dir = '${dataDir}/${wikiIdent}/underlay' + + language_default = u'${w.languageDefault}' + ${optionalString (w.superUsers != []) '' + superuser = [${concatMapStringsSep ", " uLit w.superUsers}] + ''} + + ${indentLines 4 w.extraConfig} + ''; + wikiConfigFile = name: wiki: pkgs.writeText "${name}.py" (wikiConfig name wiki); + +in +{ + options.services.moinmoin = with types; { + enable = mkEnableOption "MoinMoin Wiki Engine"; + + webServer = mkOption { + type = enum [ "nginx-gunicorn" "gunicorn" "none" ]; + default = "nginx-gunicorn"; + example = "none"; + description = '' + Which web server to use to serve the wiki. + Use <literal>none</literal> if you want to configure this yourself. + ''; + }; + + gunicorn.workers = mkOption { + type = ints.positive; + default = 3; + example = 10; + description = '' + The number of worker processes for handling requests. + ''; + }; + + wikis = mkOption { + type = attrsOf (submodule ({ name, ... }: { + options = { + siteName = mkOption { + type = str; + default = "Untitled Wiki"; + example = "ExampleWiki"; + description = '' + Short description of your wiki site, displayed below the logo on each page, and + used in RSS documents as the channel title. + ''; + }; + + webHost = mkOption { + type = str; + description = "Host part of the wiki URL. If undefined, the name of the attribute set will be used."; + example = "wiki.example.org"; + }; + + webLocation = mkOption { + type = str; + default = "/"; + example = "/moin"; + description = "Location part of the wiki URL."; + }; + + frontPage = mkOption { + type = str; + default = "LanguageSetup"; + example = "FrontPage"; + description = '' + Front page name. Set this to something like <literal>FrontPage</literal> once languages are + configured. + ''; + }; + + superUsers = mkOption { + type = listOf str; + default = []; + example = [ "elvis" ]; + description = '' + List of trusted user names with wiki system administration super powers. + + Please note that accounts for these users need to be created using the <command>moin</command> command-line utility, e.g.: + <command>moin-<replaceable>WIKINAME</replaceable> account create --name=<replaceable>NAME</replaceable> --email=<replaceable>EMAIL</replaceable> --password=<replaceable>PASSWORD</replaceable></command>. + ''; + }; + + languageDefault = mkOption { + type = str; + default = "en"; + example = "de"; + description = "The ISO-639-1 name of the main wiki language. Languages that MoinMoin does not support are ignored."; + }; + + extraConfig = mkOption { + type = lines; + default = ""; + example = '' + show_hosts = True + search_results_per_page = 100 + acl_rights_default = u"Known:read,write,delete,revert All:read" + logo_string = u"<h2>\U0001f639</h2>" + theme_default = u"modernized" + + user_checkbox_defaults = {'show_page_trail': 0, 'edit_on_doubleclick': 0} + navi_bar = [u'SomePage'] + multiconfig.DefaultConfig.navi_bar + actions_excluded = multiconfig.DefaultConfig.actions_excluded + ['newaccount'] + + mail_smarthost = "mail.example.org" + mail_from = u"Example.Org Wiki <wiki@example.org>" + ''; + description = '' + Additional configuration to be appended verbatim to this wiki's config. + + See <link xlink:href='http://moinmo.in/HelpOnConfiguration' /> for documentation. + ''; + }; + + }; + config = { + webHost = mkDefault name; + }; + })); + example = literalExample '' + { + "mywiki" = { + siteName = "Example Wiki"; + webHost = "wiki.example.org"; + superUsers = [ "admin" ]; + frontPage = "Index"; + extraConfig = "page_category_regex = ur'(?P<all>(Category|Kategorie)(?P<key>(?!Template)\S+))'" + }; + } + ''; + description = '' + Configurations of the individual wikis. Attribute names must be valid Python + identifiers of the form <literal>[A-Za-z_][A-Za-z0-9_]*</literal>. + + For every attribute <replaceable>WIKINAME</replaceable>, a helper script + moin-<replaceable>WIKINAME</replaceable> is created which runs the + <command>moin</command> command under the <literal>moin</literal> user (to avoid + file ownership issues) and with the right configuration directory passed to it. + ''; + }; + }; + + config = mkIf cfg.enable { + assertions = forEach (attrNames cfg.wikis) (wname: + { assertion = builtins.match "[A-Za-z_][A-Za-z0-9_]*" wname != null; + message = "${wname} is not valid Python identifier"; + } + ); + + users.users = { + moin = { + description = "MoinMoin wiki"; + home = dataDir; + group = group; + isSystemUser = true; + }; + }; + + users.groups = { + moin = { + members = mkIf usingNginx [ config.services.nginx.user ]; + }; + }; + + environment.systemPackages = [ pkg ] ++ map moinCliWrapper (attrNames cfg.wikis); + + systemd.services = mkIf usingGunicorn + (flip mapAttrs' cfg.wikis (wikiIdent: wiki: + nameValuePair "moin-${wikiIdent}" + { + description = "MoinMoin wiki ${wikiIdent} - gunicorn process"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + restartIfChanged = true; + restartTriggers = [ (wikiConfigFile wikiIdent wiki) ]; + + environment = let + penv = python.buildEnv.override { + # setuptools: https://github.com/benoitc/gunicorn/issues/1716 + extraLibs = [ python.pkgs.gevent python.pkgs.setuptools pkg ]; + }; + in { + PYTHONPATH = "${dataDir}/${wikiIdent}/config:${penv}/${python.sitePackages}"; + }; + + preStart = '' + umask 0007 + rm -rf ${dataDir}/${wikiIdent}/underlay + cp -r ${pkg}/share/moin/underlay ${dataDir}/${wikiIdent}/ + chmod -R u+w ${dataDir}/${wikiIdent}/underlay + ''; + + serviceConfig = { + User = user; + Group = group; + WorkingDirectory = "${dataDir}/${wikiIdent}"; + ExecStart = ''${python.pkgs.gunicorn}/bin/gunicorn moin_wsgi \ + --name gunicorn-${wikiIdent} \ + --workers ${toString cfg.gunicorn.workers} \ + --worker-class gevent \ + --bind unix:/run/moin/${wikiIdent}/gunicorn.sock + ''; + + Restart = "on-failure"; + RestartSec = "2s"; + StartLimitIntervalSec = "30s"; + + StateDirectory = "moin/${wikiIdent}"; + StateDirectoryMode = "0750"; + RuntimeDirectory = "moin/${wikiIdent}"; + RuntimeDirectoryMode = "0750"; + + NoNewPrivileges = true; + ProtectSystem = "strict"; + ProtectHome = true; + PrivateTmp = true; + PrivateDevices = true; + PrivateNetwork = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + }; + } + )); + + services.nginx = mkIf usingNginx { + enable = true; + virtualHosts = flip mapAttrs' cfg.wikis (name: w: nameValuePair w.webHost { + forceSSL = mkDefault true; + enableACME = mkDefault true; + locations."${w.webLocation}" = { + extraConfig = '' + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Server $host; + + proxy_pass http://unix:/run/moin/${name}/gunicorn.sock; + ''; + }; + }); + }; + + systemd.tmpfiles.rules = [ + "d /run/moin 0750 ${user} ${group} - -" + "d ${dataDir} 0550 ${user} ${group} - -" + ] + ++ (concatLists (flip mapAttrsToList cfg.wikis (wikiIdent: wiki: [ + "d ${dataDir}/${wikiIdent} 0750 ${user} ${group} - -" + "d ${dataDir}/${wikiIdent}/config 0550 ${user} ${group} - -" + "L+ ${dataDir}/${wikiIdent}/config/wikiconfig.py - - - - ${wikiConfigFile wikiIdent wiki}" + # needed in order to pass module name to gunicorn + "L+ ${dataDir}/${wikiIdent}/config/moin_wsgi.py - - - - ${pkg}/share/moin/server/moin.wsgi" + # seed data files + "C ${dataDir}/${wikiIdent}/data 0770 ${user} ${group} - ${pkg}/share/moin/data" + # fix nix store permissions + "Z ${dataDir}/${wikiIdent}/data 0770 ${user} ${group} - -" + ]))); + }; + + meta.maintainers = with lib.maintainers; [ b42 ]; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 744d7ed0f83..5c473f0aa89 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -170,6 +170,7 @@ in minio = handleTest ./minio.nix {}; minidlna = handleTest ./minidlna.nix {}; misc = handleTest ./misc.nix {}; + moinmoin = handleTest ./moinmoin.nix {}; mongodb = handleTest ./mongodb.nix {}; moodle = handleTest ./moodle.nix {}; morty = handleTest ./morty.nix {}; diff --git a/nixos/tests/moinmoin.nix b/nixos/tests/moinmoin.nix new file mode 100644 index 00000000000..2662b79aa09 --- /dev/null +++ b/nixos/tests/moinmoin.nix @@ -0,0 +1,24 @@ +import ./make-test.nix ({ pkgs, lib, ... }: { + name = "moinmoin"; + meta.maintainers = [ ]; # waiting for https://github.com/NixOS/nixpkgs/pull/65397 + + machine = + { ... }: + { services.moinmoin.enable = true; + services.moinmoin.wikis.ExampleWiki.superUsers = [ "admin" ]; + services.moinmoin.wikis.ExampleWiki.webHost = "localhost"; + + services.nginx.virtualHosts.localhost.enableACME = false; + services.nginx.virtualHosts.localhost.forceSSL = false; + }; + + testScript = '' + startAll; + + $machine->waitForUnit('moin-ExampleWiki.service'); + $machine->waitForUnit('nginx.service'); + $machine->waitForFile('/run/moin/ExampleWiki/gunicorn.sock'); + $machine->succeed('curl -L http://localhost/') =~ /If you have just installed/ or die; + $machine->succeed('moin-ExampleWiki account create --name=admin --email=admin@example.com --password=foo 2>&1') =~ /status success/ or die; + ''; +}) |