diff options
author | Alyssa Ross <hi@alyssa.is> | 2022-12-06 19:57:55 +0000 |
---|---|---|
committer | Alyssa Ross <hi@alyssa.is> | 2023-02-08 13:48:30 +0000 |
commit | bf3aadfdd39aa197e18bade671fab6726349ffa4 (patch) | |
tree | 698567af766ed441d757b57a7b21e68d4a342a2b /nixpkgs/nixos/modules/services/web-apps | |
parent | f4afc5a01d9539ce09e47494e679c51f80723d07 (diff) | |
parent | 99665eb45f58d959d2cb9e49ddb960c79d596f33 (diff) | |
download | nixlib-bf3aadfdd39aa197e18bade671fab6726349ffa4.tar nixlib-bf3aadfdd39aa197e18bade671fab6726349ffa4.tar.gz nixlib-bf3aadfdd39aa197e18bade671fab6726349ffa4.tar.bz2 nixlib-bf3aadfdd39aa197e18bade671fab6726349ffa4.tar.lz nixlib-bf3aadfdd39aa197e18bade671fab6726349ffa4.tar.xz nixlib-bf3aadfdd39aa197e18bade671fab6726349ffa4.tar.zst nixlib-bf3aadfdd39aa197e18bade671fab6726349ffa4.zip |
Merge commit '99665eb45f58d959d2cb9e49ddb960c79d596f33'
Diffstat (limited to 'nixpkgs/nixos/modules/services/web-apps')
72 files changed, 3611 insertions, 1902 deletions
diff --git a/nixpkgs/nixos/modules/services/web-apps/atlassian/confluence.nix b/nixpkgs/nixos/modules/services/web-apps/atlassian/confluence.nix index 2d809c17ff09..6c5de3fbe4b8 100644 --- a/nixpkgs/nixos/modules/services/web-apps/atlassian/confluence.nix +++ b/nixpkgs/nixos/modules/services/web-apps/atlassian/confluence.nix @@ -8,21 +8,22 @@ let pkg = cfg.package.override (optionalAttrs cfg.sso.enable { enableSSO = cfg.sso.enable; - crowdProperties = '' - application.name ${cfg.sso.applicationName} - application.password ${cfg.sso.applicationPassword} - application.login.url ${cfg.sso.crowd}/console/ - - crowd.server.url ${cfg.sso.crowd}/services/ - crowd.base.url ${cfg.sso.crowd}/ - - session.isauthenticated session.isauthenticated - session.tokenkey session.tokenkey - session.validationinterval ${toString cfg.sso.validationInterval} - session.lastvalidation session.lastvalidation - ''; }); + crowdProperties = pkgs.writeText "crowd.properties" '' + application.name ${cfg.sso.applicationName} + application.password ${if cfg.sso.applicationPassword != null then cfg.sso.applicationPassword else "@NIXOS_CONFLUENCE_CROWD_SSO_PWD@"} + application.login.url ${cfg.sso.crowd}/console/ + + crowd.server.url ${cfg.sso.crowd}/services/ + crowd.base.url ${cfg.sso.crowd}/ + + session.isauthenticated session.isauthenticated + session.tokenkey session.tokenkey + session.validationinterval ${toString cfg.sso.validationInterval} + session.lastvalidation session.lastvalidation + ''; + in { @@ -33,38 +34,38 @@ in user = mkOption { type = types.str; default = "confluence"; - description = "User which runs confluence."; + description = lib.mdDoc "User which runs confluence."; }; group = mkOption { type = types.str; default = "confluence"; - description = "Group which runs confluence."; + description = lib.mdDoc "Group which runs confluence."; }; home = mkOption { type = types.str; default = "/var/lib/confluence"; - description = "Home directory of the confluence instance."; + description = lib.mdDoc "Home directory of the confluence instance."; }; listenAddress = mkOption { type = types.str; default = "127.0.0.1"; - description = "Address to listen on."; + description = lib.mdDoc "Address to listen on."; }; listenPort = mkOption { type = types.int; default = 8090; - description = "Port to listen on."; + description = lib.mdDoc "Port to listen on."; }; catalinaOptions = mkOption { type = types.listOf types.str; default = []; example = [ "-Xms1024m" "-Xmx2048m" "-Dconfluence.disable.peopledirectory.all=true" ]; - description = "Java options to pass to catalina/tomcat."; + description = lib.mdDoc "Java options to pass to catalina/tomcat."; }; proxy = { @@ -73,21 +74,21 @@ in name = mkOption { type = types.str; example = "confluence.example.com"; - description = "Virtual hostname at the proxy"; + description = lib.mdDoc "Virtual hostname at the proxy"; }; port = mkOption { type = types.int; default = 443; example = 80; - description = "Port used at the proxy"; + description = lib.mdDoc "Port used at the proxy"; }; scheme = mkOption { type = types.str; default = "https"; example = "http"; - description = "Protocol used at the proxy."; + description = lib.mdDoc "Protocol used at the proxy."; }; }; @@ -97,25 +98,32 @@ in crowd = mkOption { type = types.str; example = "http://localhost:8095/crowd"; - description = "Crowd Base URL without trailing slash"; + description = lib.mdDoc "Crowd Base URL without trailing slash"; }; applicationName = mkOption { type = types.str; example = "jira"; - description = "Exact name of this Confluence instance in Crowd"; + description = lib.mdDoc "Exact name of this Confluence instance in Crowd"; }; applicationPassword = mkOption { - type = types.str; - description = "Application password of this Confluence instance in Crowd"; + type = types.nullOr types.str; + default = null; + description = lib.mdDoc "Application password of this Confluence instance in Crowd"; + }; + + applicationPasswordFile = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc "Path to the application password for Crowd of Confluence."; }; validationInterval = mkOption { type = types.int; default = 2; example = 0; - description = '' + description = lib.mdDoc '' Set to 0, if you want authentication checks to occur on each request. Otherwise set to the number of minutes between request to validate if the user is logged in or out of the Crowd SSO @@ -129,14 +137,14 @@ in type = types.package; default = pkgs.atlassian-confluence; defaultText = literalExpression "pkgs.atlassian-confluence"; - description = "Atlassian Confluence package to use."; + description = lib.mdDoc "Atlassian Confluence package to use."; }; jrePackage = mkOption { type = types.package; default = pkgs.oraclejre8; defaultText = literalExpression "pkgs.oraclejre8"; - description = "Note that Atlassian only support the Oracle JRE (JRASERVER-46152)."; + description = lib.mdDoc "Note that Atlassian only support the Oracle JRE (JRASERVER-46152)."; }; }; }; @@ -147,6 +155,16 @@ in group = cfg.group; }; + assertions = [ + { assertion = cfg.sso.enable -> ((cfg.sso.applicationPassword == null) != (cfg.sso.applicationPasswordFile)); + message = "Please set either applicationPassword or applicationPasswordFile"; + } + ]; + + warnings = mkIf (cfg.sso.enable && cfg.sso.applicationPassword != null) [ + "Using `services.confluence.sso.applicationPassword` is deprecated! Use `applicationPasswordFile` instead!" + ]; + users.groups.${cfg.group} = {}; systemd.tmpfiles.rules = [ @@ -173,6 +191,7 @@ in CONF_USER = cfg.user; JAVA_HOME = "${cfg.jrePackage}"; CATALINA_OPTS = concatStringsSep " " cfg.catalinaOptions; + JAVA_OPTS = mkIf cfg.sso.enable "-Dcrowd.properties=${cfg.home}/crowd.properties"; }; preStart = '' @@ -183,12 +202,24 @@ in -e 's,protocol="org.apache.coyote.http11.Http11NioProtocol",protocol="org.apache.coyote.http11.Http11NioProtocol" proxyName="${cfg.proxy.name}" proxyPort="${toString cfg.proxy.port}" scheme="${cfg.proxy.scheme}",' \ '') + '' ${pkg}/conf/server.xml.dist > ${cfg.home}/server.xml + + ${optionalString cfg.sso.enable '' + install -m660 ${crowdProperties} ${cfg.home}/crowd.properties + ${optionalString (cfg.sso.applicationPasswordFile != null) '' + ${pkgs.replace-secret}/bin/replace-secret \ + '@NIXOS_CONFLUENCE_CROWD_SSO_PWD@' \ + ${cfg.sso.applicationPasswordFile} \ + ${cfg.home}/crowd.properties + ''} + ''} ''; serviceConfig = { User = cfg.user; Group = cfg.group; PrivateTmp = true; + Restart = "on-failure"; + RestartSec = "10"; ExecStart = "${pkg}/bin/start-confluence.sh -fg"; ExecStop = "${pkg}/bin/stop-confluence.sh"; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/atlassian/crowd.nix b/nixpkgs/nixos/modules/services/web-apps/atlassian/crowd.nix index a8b2482d5a9c..abe3a8bdb225 100644 --- a/nixpkgs/nixos/modules/services/web-apps/atlassian/crowd.nix +++ b/nixpkgs/nixos/modules/services/web-apps/atlassian/crowd.nix @@ -14,6 +14,21 @@ let proxyUrl = "${cfg.proxy.scheme}://${cfg.proxy.name}:${toString cfg.proxy.port}"; }); + crowdPropertiesFile = pkgs.writeText "crowd.properties" '' + application.name crowd-openid-server + application.password @NIXOS_CROWD_OPENID_PW@ + application.base.url http://localhost:${toString cfg.listenPort}/openidserver + application.login.url http://localhost:${toString cfg.listenPort}/openidserver + application.login.url.template http://localhost:${toString cfg.listenPort}/openidserver?returnToUrl=''${RETURN_TO_URL} + + crowd.server.url http://localhost:${toString cfg.listenPort}/crowd/services/ + + session.isauthenticated session.isauthenticated + session.tokenkey session.tokenkey + session.validationinterval 0 + session.lastvalidation session.lastvalidation + ''; + in { @@ -24,43 +39,50 @@ in user = mkOption { type = types.str; default = "crowd"; - description = "User which runs Crowd."; + description = lib.mdDoc "User which runs Crowd."; }; group = mkOption { type = types.str; default = "crowd"; - description = "Group which runs Crowd."; + description = lib.mdDoc "Group which runs Crowd."; }; home = mkOption { type = types.str; default = "/var/lib/crowd"; - description = "Home directory of the Crowd instance."; + description = lib.mdDoc "Home directory of the Crowd instance."; }; listenAddress = mkOption { type = types.str; default = "127.0.0.1"; - description = "Address to listen on."; + description = lib.mdDoc "Address to listen on."; }; listenPort = mkOption { type = types.int; default = 8092; - description = "Port to listen on."; + description = lib.mdDoc "Port to listen on."; }; openidPassword = mkOption { type = types.str; - description = "Application password for OpenID server."; + default = "WILL_NEVER_BE_SET"; + description = lib.mdDoc "Application password for OpenID server."; + }; + + openidPasswordFile = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc "Path to the file containing the application password for OpenID server."; }; catalinaOptions = mkOption { type = types.listOf types.str; default = []; example = [ "-Xms1024m" "-Xmx2048m" ]; - description = "Java options to pass to catalina/tomcat."; + description = lib.mdDoc "Java options to pass to catalina/tomcat."; }; proxy = { @@ -69,27 +91,27 @@ in name = mkOption { type = types.str; example = "crowd.example.com"; - description = "Virtual hostname at the proxy"; + description = lib.mdDoc "Virtual hostname at the proxy"; }; port = mkOption { type = types.int; default = 443; example = 80; - description = "Port used at the proxy"; + description = lib.mdDoc "Port used at the proxy"; }; scheme = mkOption { type = types.str; default = "https"; example = "http"; - description = "Protocol used at the proxy."; + description = lib.mdDoc "Protocol used at the proxy."; }; secure = mkOption { type = types.bool; default = true; - description = "Whether the connections to the proxy should be considered secure."; + description = lib.mdDoc "Whether the connections to the proxy should be considered secure."; }; }; @@ -97,14 +119,14 @@ in type = types.package; default = pkgs.atlassian-crowd; defaultText = literalExpression "pkgs.atlassian-crowd"; - description = "Atlassian Crowd package to use."; + description = lib.mdDoc "Atlassian Crowd package to use."; }; jrePackage = mkOption { type = types.package; default = pkgs.oraclejre8; defaultText = literalExpression "pkgs.oraclejre8"; - description = "Note that Atlassian only support the Oracle JRE (JRASERVER-46152)."; + description = lib.mdDoc "Note that Atlassian only support the Oracle JRE (JRASERVER-46152)."; }; }; }; @@ -140,6 +162,7 @@ in JAVA_HOME = "${cfg.jrePackage}"; CATALINA_OPTS = concatStringsSep " " cfg.catalinaOptions; CATALINA_TMPDIR = "/tmp"; + JAVA_OPTS = mkIf (cfg.openidPasswordFile != null) "-Dcrowd.properties=${cfg.home}/crowd.properties"; }; preStart = '' @@ -151,12 +174,22 @@ in -e 's,compression="on",compression="off" protocol="HTTP/1.1" proxyName="${cfg.proxy.name}" proxyPort="${toString cfg.proxy.port}" scheme="${cfg.proxy.scheme}" secure="${boolToString cfg.proxy.secure}",' \ '') + '' ${pkg}/apache-tomcat/conf/server.xml.dist > ${cfg.home}/server.xml + + ${optionalString (cfg.openidPasswordFile != null) '' + install -m660 ${crowdPropertiesFile} ${cfg.home}/crowd.properties + ${pkgs.replace-secret}/bin/replace-secret \ + '@NIXOS_CROWD_OPENID_PW@' \ + ${cfg.openidPasswordFile} \ + ${cfg.home}/crowd.properties + ''} ''; serviceConfig = { User = cfg.user; Group = cfg.group; PrivateTmp = true; + Restart = "on-failure"; + RestartSec = "10"; ExecStart = "${pkg}/start_crowd.sh -fg"; }; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/atlassian/jira.nix b/nixpkgs/nixos/modules/services/web-apps/atlassian/jira.nix index d7a26838d6f8..5d62160ffb13 100644 --- a/nixpkgs/nixos/modules/services/web-apps/atlassian/jira.nix +++ b/nixpkgs/nixos/modules/services/web-apps/atlassian/jira.nix @@ -8,21 +8,22 @@ let pkg = cfg.package.override (optionalAttrs cfg.sso.enable { enableSSO = cfg.sso.enable; - crowdProperties = '' - application.name ${cfg.sso.applicationName} - application.password ${cfg.sso.applicationPassword} - application.login.url ${cfg.sso.crowd}/console/ - - crowd.server.url ${cfg.sso.crowd}/services/ - crowd.base.url ${cfg.sso.crowd}/ - - session.isauthenticated session.isauthenticated - session.tokenkey session.tokenkey - session.validationinterval ${toString cfg.sso.validationInterval} - session.lastvalidation session.lastvalidation - ''; }); + crowdProperties = pkgs.writeText "crowd.properties" '' + application.name ${cfg.sso.applicationName} + application.password @NIXOS_JIRA_CROWD_SSO_PWD@ + application.login.url ${cfg.sso.crowd}/console/ + + crowd.server.url ${cfg.sso.crowd}/services/ + crowd.base.url ${cfg.sso.crowd}/ + + session.isauthenticated session.isauthenticated + session.tokenkey session.tokenkey + session.validationinterval ${toString cfg.sso.validationInterval} + session.lastvalidation session.lastvalidation + ''; + in { @@ -33,38 +34,38 @@ in user = mkOption { type = types.str; default = "jira"; - description = "User which runs JIRA."; + description = lib.mdDoc "User which runs JIRA."; }; group = mkOption { type = types.str; default = "jira"; - description = "Group which runs JIRA."; + description = lib.mdDoc "Group which runs JIRA."; }; home = mkOption { type = types.str; default = "/var/lib/jira"; - description = "Home directory of the JIRA instance."; + description = lib.mdDoc "Home directory of the JIRA instance."; }; listenAddress = mkOption { type = types.str; default = "127.0.0.1"; - description = "Address to listen on."; + description = lib.mdDoc "Address to listen on."; }; listenPort = mkOption { type = types.int; default = 8091; - description = "Port to listen on."; + description = lib.mdDoc "Port to listen on."; }; catalinaOptions = mkOption { type = types.listOf types.str; default = []; example = [ "-Xms1024m" "-Xmx2048m" ]; - description = "Java options to pass to catalina/tomcat."; + description = lib.mdDoc "Java options to pass to catalina/tomcat."; }; proxy = { @@ -73,27 +74,27 @@ in name = mkOption { type = types.str; example = "jira.example.com"; - description = "Virtual hostname at the proxy"; + description = lib.mdDoc "Virtual hostname at the proxy"; }; port = mkOption { type = types.int; default = 443; example = 80; - description = "Port used at the proxy"; + description = lib.mdDoc "Port used at the proxy"; }; scheme = mkOption { type = types.str; default = "https"; example = "http"; - description = "Protocol used at the proxy."; + description = lib.mdDoc "Protocol used at the proxy."; }; secure = mkOption { type = types.bool; default = true; - description = "Whether the connections to the proxy should be considered secure."; + description = lib.mdDoc "Whether the connections to the proxy should be considered secure."; }; }; @@ -103,25 +104,25 @@ in crowd = mkOption { type = types.str; example = "http://localhost:8095/crowd"; - description = "Crowd Base URL without trailing slash"; + description = lib.mdDoc "Crowd Base URL without trailing slash"; }; applicationName = mkOption { type = types.str; example = "jira"; - description = "Exact name of this JIRA instance in Crowd"; + description = lib.mdDoc "Exact name of this JIRA instance in Crowd"; }; - applicationPassword = mkOption { + applicationPasswordFile = mkOption { type = types.str; - description = "Application password of this JIRA instance in Crowd"; + description = lib.mdDoc "Path to the file containing the application password of this JIRA instance in Crowd"; }; validationInterval = mkOption { type = types.int; default = 2; example = 0; - description = '' + description = lib.mdDoc '' Set to 0, if you want authentication checks to occur on each request. Otherwise set to the number of minutes between request to validate if the user is logged in or out of the Crowd SSO @@ -135,14 +136,14 @@ in type = types.package; default = pkgs.atlassian-jira; defaultText = literalExpression "pkgs.atlassian-jira"; - description = "Atlassian JIRA package to use."; + description = lib.mdDoc "Atlassian JIRA package to use."; }; jrePackage = mkOption { type = types.package; default = pkgs.oraclejre8; defaultText = literalExpression "pkgs.oraclejre8"; - description = "Note that Atlassian only support the Oracle JRE (JRASERVER-46152)."; + description = lib.mdDoc "Note that Atlassian only support the Oracle JRE (JRASERVER-46152)."; }; }; }; @@ -151,6 +152,7 @@ in users.users.${cfg.user} = { isSystemUser = true; group = cfg.group; + home = cfg.home; }; users.groups.${cfg.group} = {}; @@ -180,6 +182,7 @@ in JIRA_HOME = cfg.home; JAVA_HOME = "${cfg.jrePackage}"; CATALINA_OPTS = concatStringsSep " " cfg.catalinaOptions; + JAVA_OPTS = mkIf cfg.sso.enable "-Dcrowd.properties=${cfg.home}/crowd.properties"; }; preStart = '' @@ -190,15 +193,31 @@ in -e 's,protocol="HTTP/1.1",protocol="HTTP/1.1" proxyName="${cfg.proxy.name}" proxyPort="${toString cfg.proxy.port}" scheme="${cfg.proxy.scheme}" secure="${toString cfg.proxy.secure}",' \ '') + '' ${pkg}/conf/server.xml.dist > ${cfg.home}/server.xml + + ${optionalString cfg.sso.enable '' + install -m660 ${crowdProperties} ${cfg.home}/crowd.properties + ${pkgs.replace-secret}/bin/replace-secret \ + '@NIXOS_JIRA_CROWD_SSO_PWD@' \ + ${cfg.sso.applicationPasswordFile} \ + ${cfg.home}/crowd.properties + ''} ''; serviceConfig = { User = cfg.user; Group = cfg.group; PrivateTmp = true; + Restart = "on-failure"; + RestartSec = "10"; ExecStart = "${pkg}/bin/start-jira.sh -fg"; ExecStop = "${pkg}/bin/stop-jira.sh"; }; }; }; + + imports = [ + (mkRemovedOptionModule [ "services" "jira" "sso" "applicationPassword" ] '' + Use `applicationPasswordFile` instead! + '') + ]; } diff --git a/nixpkgs/nixos/modules/services/web-apps/baget.nix b/nixpkgs/nixos/modules/services/web-apps/baget.nix index 3007dd4fbb26..dd70d462d57d 100644 --- a/nixpkgs/nixos/modules/services/web-apps/baget.nix +++ b/nixpkgs/nixos/modules/services/web-apps/baget.nix @@ -58,7 +58,7 @@ in apiKeyFile = mkOption { type = types.path; example = "/root/baget.key"; - description = '' + description = lib.mdDoc '' Private API key for BaGet. ''; }; @@ -112,8 +112,8 @@ in }; } ''; - description = '' - Extra configuration options for BaGet. Refer to <link xlink:href="https://loic-sharma.github.io/BaGet/configuration/"/> for details. + description = lib.mdDoc '' + Extra configuration options for BaGet. Refer to <https://loic-sharma.github.io/BaGet/configuration/> for details. Default value is merged with values from here. ''; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/bookstack.nix b/nixpkgs/nixos/modules/services/web-apps/bookstack.nix index 64a2767fab6e..b939adc50fa3 100644 --- a/nixpkgs/nixos/modules/services/web-apps/bookstack.nix +++ b/nixpkgs/nixos/modules/services/web-apps/bookstack.nix @@ -38,21 +38,21 @@ in { user = mkOption { default = "bookstack"; - description = "User bookstack runs as."; + description = lib.mdDoc "User bookstack runs as."; type = types.str; }; group = mkOption { default = "bookstack"; - description = "Group bookstack runs as."; + description = lib.mdDoc "Group bookstack runs as."; type = types.str; }; appKeyFile = mkOption { - description = '' + description = lib.mdDoc '' A file containing the Laravel APP_KEY - a 32 character long, base64 encoded key used for encryption where needed. Can be - generated with <code>head -c 32 /dev/urandom | base64</code>. + generated with `head -c 32 /dev/urandom | base64`. ''; example = "/run/keys/bookstack-appkey"; type = types.path; @@ -66,15 +66,15 @@ in { config.networking.hostName; defaultText = lib.literalExpression "config.networking.fqdn"; example = "bookstack.example.com"; - description = '' + description = lib.mdDoc '' The hostname to serve BookStack on. ''; }; appURL = mkOption { - description = '' + description = lib.mdDoc '' The root URL that you want to host BookStack on. All URLs in BookStack will be generated using this value. - If you change this in the future you may need to run a command to update stored URLs in the database. Command example: <code>php artisan bookstack:update-url https://old.example.com https://new.example.com</code> + If you change this in the future you may need to run a command to update stored URLs in the database. Command example: `php artisan bookstack:update-url https://old.example.com https://new.example.com` ''; default = "http${lib.optionalString tlsEnabled "s"}://${cfg.hostname}"; defaultText = ''http''${lib.optionalString tlsEnabled "s"}://''${cfg.hostname}''; @@ -83,7 +83,7 @@ in { }; dataDir = mkOption { - description = "BookStack data directory"; + description = lib.mdDoc "BookStack data directory"; default = "/var/lib/bookstack"; type = types.path; }; @@ -92,37 +92,37 @@ in { host = mkOption { type = types.str; default = "localhost"; - description = "Database host address."; + description = lib.mdDoc "Database host address."; }; port = mkOption { type = types.port; default = 3306; - description = "Database host port."; + description = lib.mdDoc "Database host port."; }; name = mkOption { type = types.str; default = "bookstack"; - description = "Database name."; + description = lib.mdDoc "Database name."; }; user = mkOption { type = types.str; default = user; defaultText = literalExpression "user"; - description = "Database username."; + description = lib.mdDoc "Database username."; }; passwordFile = mkOption { type = with types; nullOr path; default = null; example = "/run/keys/bookstack-dbpassword"; - description = '' + description = lib.mdDoc '' A file containing the password corresponding to - <option>database.user</option>. + {option}`database.user`. ''; }; createLocally = mkOption { type = types.bool; default = false; - description = "Create the database and database user locally."; + description = lib.mdDoc "Create the database and database user locally."; }; }; @@ -130,47 +130,47 @@ in { driver = mkOption { type = types.enum [ "smtp" "sendmail" ]; default = "smtp"; - description = "Mail driver to use."; + description = lib.mdDoc "Mail driver to use."; }; host = mkOption { type = types.str; default = "localhost"; - description = "Mail host address."; + description = lib.mdDoc "Mail host address."; }; port = mkOption { type = types.port; default = 1025; - description = "Mail host port."; + description = lib.mdDoc "Mail host port."; }; fromName = mkOption { type = types.str; default = "BookStack"; - description = "Mail \"from\" name."; + description = lib.mdDoc "Mail \"from\" name."; }; from = mkOption { type = types.str; default = "mail@bookstackapp.com"; - description = "Mail \"from\" email."; + description = lib.mdDoc "Mail \"from\" email."; }; user = mkOption { type = with types; nullOr str; default = null; example = "bookstack"; - description = "Mail username."; + description = lib.mdDoc "Mail username."; }; passwordFile = mkOption { type = with types; nullOr path; default = null; example = "/run/keys/bookstack-mailpassword"; - description = '' + description = lib.mdDoc '' A file containing the password corresponding to - <option>mail.user</option>. + {option}`mail.user`. ''; }; encryption = mkOption { type = with types; nullOr (enum [ "tls" ]); default = null; - description = "SMTP encryption mechanism to use."; + description = lib.mdDoc "SMTP encryption mechanism to use."; }; }; @@ -178,7 +178,7 @@ in { type = types.str; default = "18M"; example = "1G"; - description = "The maximum size for uploads (e.g. images)."; + description = lib.mdDoc "The maximum size for uploads (e.g. images)."; }; poolConfig = mkOption { @@ -191,8 +191,8 @@ in { "pm.max_spare_servers" = 4; "pm.max_requests" = 500; }; - description = '' - Options for the bookstack PHP pool. See the documentation on <literal>php-fpm.conf</literal> + description = lib.mdDoc '' + Options for the bookstack PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives. ''; }; @@ -213,7 +213,7 @@ in { enableACME = true; } ''; - description = '' + description = lib.mdDoc '' With this option, you can customize the nginx virtualHost settings. ''; }; @@ -256,20 +256,20 @@ in { OIDC_ISSUER_DISCOVER = true; } ''; - description = '' + description = lib.mdDoc '' BookStack configuration options to set in the - <filename>.env</filename> file. + {file}`.env` file. - Refer to <link xlink:href="https://www.bookstackapp.com/docs/"/> + Refer to <https://www.bookstackapp.com/docs/> for details on supported values. Settings containing secret data should be set to an attribute - set containing the attribute <literal>_secret</literal> - a + set containing the attribute `_secret` - a string pointing to a file containing the value the option should be set to. See the example to get a better picture of - this: in the resulting <filename>.env</filename> file, the - <literal>OIDC_CLIENT_SECRET</literal> key will be set to the - contents of the <filename>/run/keys/oidc_secret</filename> + this: in the resulting {file}`.env` file, the + `OIDC_CLIENT_SECRET` key will be set to the + contents of the {file}`/run/keys/oidc_secret` file. ''; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/calibre-web.nix b/nixpkgs/nixos/modules/services/web-apps/calibre-web.nix index 704cd2cfa8a7..6bcf733452b9 100644 --- a/nixpkgs/nixos/modules/services/web-apps/calibre-web.nix +++ b/nixpkgs/nixos/modules/services/web-apps/calibre-web.nix @@ -14,7 +14,7 @@ in ip = mkOption { type = types.str; default = "::1"; - description = '' + description = lib.mdDoc '' IP address that Calibre-Web should listen on. ''; }; @@ -22,7 +22,7 @@ in port = mkOption { type = types.port; default = 8083; - description = '' + description = lib.mdDoc '' Listen port for Calibre-Web. ''; }; @@ -31,27 +31,27 @@ in dataDir = mkOption { type = types.str; default = "calibre-web"; - description = '' - The directory below <filename>/var/lib</filename> where Calibre-Web stores its data. + description = lib.mdDoc '' + The directory below {file}`/var/lib` where Calibre-Web stores its data. ''; }; user = mkOption { type = types.str; default = "calibre-web"; - description = "User account under which Calibre-Web runs."; + description = lib.mdDoc "User account under which Calibre-Web runs."; }; group = mkOption { type = types.str; default = "calibre-web"; - description = "Group account under which Calibre-Web runs."; + description = lib.mdDoc "Group account under which Calibre-Web runs."; }; openFirewall = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Open ports in the firewall for the server. ''; }; @@ -60,7 +60,7 @@ in calibreLibrary = mkOption { type = types.nullOr types.path; default = null; - description = '' + description = lib.mdDoc '' Path to Calibre library. ''; }; @@ -68,7 +68,7 @@ in enableBookConversion = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Configure path to the Calibre's ebook-convert in the DB. ''; }; @@ -76,7 +76,7 @@ in enableBookUploading = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Allow books to be uploaded via Calibre-Web UI. ''; }; @@ -85,7 +85,7 @@ in enable = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Enable authorization using auth proxy. ''; }; @@ -93,7 +93,7 @@ in header = mkOption { type = types.str; default = ""; - description = '' + description = lib.mdDoc '' Auth proxy header name. ''; }; @@ -136,7 +136,7 @@ in ${pkgs.sqlite}/bin/sqlite3 ${appDb} "update settings set ${settings}" '' + optionalString (cfg.options.calibreLibrary != null) '' - test -f ${cfg.options.calibreLibrary}/metadata.db || { echo "Invalid Calibre library"; exit 1; } + test -f "${cfg.options.calibreLibrary}/metadata.db" || { echo "Invalid Calibre library"; exit 1; } '' ); diff --git a/nixpkgs/nixos/modules/services/web-apps/code-server.nix b/nixpkgs/nixos/modules/services/web-apps/code-server.nix index 474e9140ae87..84fc03deabff 100644 --- a/nixpkgs/nixos/modules/services/web-apps/code-server.nix +++ b/nixpkgs/nixos/modules/services/web-apps/code-server.nix @@ -16,13 +16,13 @@ in { package = mkOption { default = pkgs.code-server; defaultText = "pkgs.code-server"; - description = "Which code-server derivation to use."; + description = lib.mdDoc "Which code-server derivation to use."; type = types.package; }; extraPackages = mkOption { default = [ ]; - description = "Packages that are available in the PATH of code-server."; + description = lib.mdDoc "Packages that are available in the PATH of code-server."; example = "[ pkgs.go ]"; type = types.listOf types.package; }; @@ -30,49 +30,49 @@ in { extraEnvironment = mkOption { type = types.attrsOf types.str; description = - "Additional environment variables to passed to code-server."; + lib.mdDoc "Additional environment variables to passed to code-server."; default = { }; example = { PKG_CONFIG_PATH = "/run/current-system/sw/lib/pkgconfig"; }; }; extraArguments = mkOption { default = [ "--disable-telemetry" ]; - description = "Additional arguments that passed to code-server"; + description = lib.mdDoc "Additional arguments that passed to code-server"; example = ''[ "--verbose" ]''; type = types.listOf types.str; }; host = mkOption { default = "127.0.0.1"; - description = "The host-ip to bind to."; + description = lib.mdDoc "The host-ip to bind to."; type = types.str; }; port = mkOption { default = 4444; - description = "The port where code-server runs."; + description = lib.mdDoc "The port where code-server runs."; type = types.port; }; auth = mkOption { default = "password"; - description = "The type of authentication to use."; + description = lib.mdDoc "The type of authentication to use."; type = types.enum [ "none" "password" ]; }; hashedPassword = mkOption { default = ""; description = - "Create the password with: 'echo -n 'thisismypassword' | npx argon2-cli -e'."; + lib.mdDoc "Create the password with: 'echo -n 'thisismypassword' | npx argon2-cli -e'."; type = types.str; }; user = mkOption { default = defaultUser; example = "yourUser"; - description = '' + description = lib.mdDoc '' The user to run code-server as. - By default, a user named <literal>${defaultUser}</literal> will be created. + By default, a user named `${defaultUser}` will be created. ''; type = types.str; }; @@ -80,9 +80,9 @@ in { group = mkOption { default = defaultGroup; example = "yourGroup"; - description = '' + description = lib.mdDoc '' The group to run code-server under. - By default, a group named <literal>${defaultGroup}</literal> will be created. + By default, a group named `${defaultGroup}` will be created. ''; type = types.str; }; @@ -90,7 +90,7 @@ in { extraGroups = mkOption { default = [ ]; description = - "An array of additional groups for the <literal>${defaultUser}</literal> user."; + lib.mdDoc "An array of additional groups for the `${defaultUser}` user."; example = [ "docker" ]; type = types.listOf types.str; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/convos.nix b/nixpkgs/nixos/modules/services/web-apps/convos.nix index 8be11eec9f31..120481c64017 100644 --- a/nixpkgs/nixos/modules/services/web-apps/convos.nix +++ b/nixpkgs/nixos/modules/services/web-apps/convos.nix @@ -12,21 +12,21 @@ in type = types.port; default = 3000; example = 8080; - description = "Port the web interface should listen on"; + description = lib.mdDoc "Port the web interface should listen on"; }; listenAddress = mkOption { type = types.str; default = "*"; example = "127.0.0.1"; - description = "Address or host the web interface should listen on"; + description = lib.mdDoc "Address or host the web interface should listen on"; }; reverseProxy = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Enables reverse proxy support. This will allow Convos to automatically - pick up the <literal>X-Forwarded-For</literal> and - <literal>X-Request-Base</literal> HTTP headers set in your reverse proxy + pick up the `X-Forwarded-For` and + `X-Request-Base` HTTP headers set in your reverse proxy web server. Note that enabling this option without a reverse proxy in front will be a security issue. ''; diff --git a/nixpkgs/nixos/modules/services/web-apps/cryptpad.nix b/nixpkgs/nixos/modules/services/web-apps/cryptpad.nix deleted file mode 100644 index e6772de768e0..000000000000 --- a/nixpkgs/nixos/modules/services/web-apps/cryptpad.nix +++ /dev/null @@ -1,54 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - cfg = config.services.cryptpad; -in -{ - options.services.cryptpad = { - enable = mkEnableOption "the Cryptpad service"; - - package = mkOption { - default = pkgs.cryptpad; - defaultText = literalExpression "pkgs.cryptpad"; - type = types.package; - description = " - Cryptpad package to use. - "; - }; - - configFile = mkOption { - type = types.path; - default = "${cfg.package}/lib/node_modules/cryptpad/config/config.example.js"; - defaultText = literalExpression ''"''${package}/lib/node_modules/cryptpad/config/config.example.js"''; - description = '' - Path to the JavaScript configuration file. - - See <link - xlink:href="https://github.com/xwiki-labs/cryptpad/blob/master/config/config.example.js"/> - for a configuration example. - ''; - }; - }; - - config = mkIf cfg.enable { - systemd.services.cryptpad = { - description = "Cryptpad Service"; - wantedBy = [ "multi-user.target" ]; - after = [ "networking.target" ]; - serviceConfig = { - DynamicUser = true; - Environment = [ - "CRYPTPAD_CONFIG=${cfg.configFile}" - "HOME=%S/cryptpad" - ]; - ExecStart = "${cfg.package}/bin/cryptpad"; - PrivateTmp = true; - Restart = "always"; - StateDirectory = "cryptpad"; - WorkingDirectory = "%S/cryptpad"; - }; - }; - }; -} diff --git a/nixpkgs/nixos/modules/services/web-apps/dex.nix b/nixpkgs/nixos/modules/services/web-apps/dex.nix index 4d4689a4cf24..82fdcd212f96 100644 --- a/nixpkgs/nixos/modules/services/web-apps/dex.nix +++ b/nixpkgs/nixos/modules/services/web-apps/dex.nix @@ -11,15 +11,26 @@ let settingsFormat = pkgs.formats.yaml {}; configFile = settingsFormat.generate "config.yaml" filteredSettings; - startPreScript = pkgs.writeShellScript "dex-start-pre" ('' - '' + (concatStringsSep "\n" (builtins.map (file: '' - ${pkgs.replace-secret}/bin/replace-secret '${file}' '${file}' /run/dex/config.yaml - '') secretFiles))); + startPreScript = pkgs.writeShellScript "dex-start-pre" + (concatStringsSep "\n" (map (file: '' + replace-secret '${file}' '${file}' /run/dex/config.yaml + '') + secretFiles)); in { options.services.dex = { enable = mkEnableOption "the OpenID Connect and OAuth2 identity provider"; + environmentFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Environment file (see <literal>systemd.exec(5)</literal> + "EnvironmentFile=" section for the syntax) to define variables for dex. + This option can be used to safely include secret keys into the dex configuration. + ''; + }; + settings = mkOption { type = settingsFormat.type; default = {}; @@ -45,9 +56,12 @@ in ]; } ''; - description = '' + description = lib.mdDoc '' The available options can be found in - <link xlink:href="https://github.com/dexidp/dex/blob/v${pkgs.dex.version}/config.yaml.dist">the example configuration</link>. + [the example configuration](https://github.com/dexidp/dex/blob/v${pkgs.dex.version}/config.yaml.dist). + + It's also possible to refer to environment variables (defined in [services.dex.environmentFile](#opt-services.dex.environmentFile)) + using the syntax `$VARIABLE_NAME`. ''; }; }; @@ -57,15 +71,15 @@ in description = "dex identity provider"; wantedBy = [ "multi-user.target" ]; after = [ "networking.target" ] ++ (optional (cfg.settings.storage.type == "postgres") "postgresql.service"); - + path = with pkgs; [ replace-secret ]; serviceConfig = { ExecStart = "${pkgs.dex-oidc}/bin/dex serve /run/dex/config.yaml"; ExecStartPre = [ "${pkgs.coreutils}/bin/install -m 600 ${configFile} /run/dex/config.yaml" "+${startPreScript}" ]; - RuntimeDirectory = "dex"; + RuntimeDirectory = "dex"; AmbientCapabilities = "CAP_NET_BIND_SERVICE"; BindReadOnlyPaths = [ "/nix/store" @@ -109,6 +123,8 @@ in TemporaryFileSystem = "/:ro"; # Does not work well with the temporary root #UMask = "0066"; + } // optionalAttrs (cfg.environmentFile != null) { + EnvironmentFile = cfg.environmentFile; }; }; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/discourse.nix b/nixpkgs/nixos/modules/services/web-apps/discourse.nix index 2c2911aada3f..1e2326d81801 100644 --- a/nixpkgs/nixos/modules/services/web-apps/discourse.nix +++ b/nixpkgs/nixos/modules/services/web-apps/discourse.nix @@ -6,7 +6,7 @@ let cfg = config.services.discourse; opt = options.services.discourse; - # Keep in sync with https://github.com/discourse/discourse_docker/blob/master/image/base/Dockerfile#L5 + # Keep in sync with https://github.com/discourse/discourse_docker/blob/main/image/base/slim.Dockerfile#L5 upstreamPostgresqlVersion = lib.getVersion pkgs.postgresql_13; postgresqlPackage = if config.services.postgresql.enable then @@ -35,7 +35,7 @@ in plugins = lib.unique (p.enabledPlugins ++ cfg.plugins); }; defaultText = lib.literalExpression "pkgs.discourse"; - description = '' + description = lib.mdDoc '' The discourse package to use. ''; }; @@ -48,7 +48,7 @@ in config.networking.hostName; defaultText = lib.literalExpression "config.networking.fqdn"; example = "discourse.example.com"; - description = '' + description = lib.mdDoc '' The hostname to serve Discourse on. ''; }; @@ -81,7 +81,7 @@ in type = with lib.types; nullOr path; default = null; example = "/run/keys/ssl.cert"; - description = '' + description = lib.mdDoc '' The path to the server SSL certificate. Set this to enable SSL. ''; @@ -91,7 +91,7 @@ in type = with lib.types; nullOr path; default = null; example = "/run/keys/ssl.key"; - description = '' + description = lib.mdDoc '' The path to the server SSL certificate key. Set this to enable SSL. ''; @@ -104,7 +104,7 @@ in <literal>true</literal>, unless <option>services.discourse.sslCertificate</option> and <option>services.discourse.sslCertificateKey</option> are set. ''; - description = '' + description = lib.mdDoc '' Whether an ACME certificate should be used to secure connections to the server. ''; @@ -151,26 +151,26 @@ in }; }; ''; - description = '' + description = lib.mdDoc '' Discourse site settings. These are the settings that can be changed from the UI. This only defines their default values: they can still be overridden from the UI. Available settings can be found by looking in the - <link xlink:href="https://github.com/discourse/discourse/blob/master/config/site_settings.yml">site_settings.yml</link> + [site_settings.yml](https://github.com/discourse/discourse/blob/master/config/site_settings.yml) file of the upstream distribution. To find a setting's path, you only need to care about the first two levels; i.e. its category and name. See the example. Settings containing secret data should be set to an attribute set containing the attribute - <literal>_secret</literal> - a string pointing to a file + `_secret` - a string pointing to a file containing the value the option should be set to. See the example to get a better picture of this: in the resulting - <filename>config/nixos_site_settings.json</filename> file, - the <literal>login.github_client_secret</literal> key will + {file}`config/nixos_site_settings.json` file, + the `login.github_client_secret` key will be set to the contents of the - <filename>/run/keys/discourse_github_client_secret</filename> + {file}`/run/keys/discourse_github_client_secret` file. ''; }; @@ -179,7 +179,7 @@ in skipCreate = lib.mkOption { type = lib.types.bool; default = false; - description = '' + description = lib.mdDoc '' Do not create the admin account, instead rely on other existing admin accounts. ''; @@ -188,7 +188,7 @@ in email = lib.mkOption { type = lib.types.str; example = "admin@example.com"; - description = '' + description = lib.mdDoc '' The admin user email address. ''; }; @@ -196,21 +196,21 @@ in username = lib.mkOption { type = lib.types.str; example = "admin"; - description = '' + description = lib.mdDoc '' The admin user username. ''; }; fullName = lib.mkOption { type = lib.types.str; - description = '' + description = lib.mdDoc '' The admin user's full name. ''; }; passwordFile = lib.mkOption { type = lib.types.path; - description = '' + description = lib.mdDoc '' A path to a file containing the admin user's password. This should be a string, not a nix path, since nix paths are @@ -222,8 +222,8 @@ in nginx.enable = lib.mkOption { type = lib.types.bool; default = true; - description = '' - Whether an <literal>nginx</literal> virtual host should be + description = lib.mdDoc '' + Whether an `nginx` virtual host should be set up to serve Discourse. Only disable if you're planning to use a different web server, which is not recommended. ''; @@ -233,7 +233,7 @@ in pool = lib.mkOption { type = lib.types.int; default = 8; - description = '' + description = lib.mdDoc '' Database connection pool size. ''; }; @@ -250,7 +250,7 @@ in passwordFile = lib.mkOption { type = with lib.types; nullOr path; default = null; - description = '' + description = lib.mdDoc '' File containing the Discourse database user password. This should be a string, not a nix path, since nix paths are @@ -261,18 +261,18 @@ in createLocally = lib.mkOption { type = lib.types.bool; default = true; - description = '' + description = lib.mdDoc '' Whether a database should be automatically created on the - local host. Set this to <literal>false</literal> if you plan + local host. Set this to `false` if you plan on provisioning a local database yourself. This has no effect - if <option>services.discourse.database.host</option> is customized. + if {option}`services.discourse.database.host` is customized. ''; }; name = lib.mkOption { type = lib.types.str; default = "discourse"; - description = '' + description = lib.mdDoc '' Discourse database name. ''; }; @@ -280,7 +280,7 @@ in username = lib.mkOption { type = lib.types.str; default = "discourse"; - description = '' + description = lib.mdDoc '' Discourse database user. ''; }; @@ -288,10 +288,10 @@ in ignorePostgresqlVersion = lib.mkOption { type = lib.types.bool; default = false; - description = '' + description = lib.mdDoc '' Whether to allow other versions of PostgreSQL than the recommended one. Only effective when - <option>services.discourse.database.createLocally</option> + {option}`services.discourse.database.createLocally` is enabled. ''; }; @@ -301,7 +301,7 @@ in host = lib.mkOption { type = lib.types.str; default = "localhost"; - description = '' + description = lib.mdDoc '' Redis server hostname. ''; }; @@ -309,7 +309,7 @@ in passwordFile = lib.mkOption { type = with lib.types; nullOr path; default = null; - description = '' + description = lib.mdDoc '' File containing the Redis password. This should be a string, not a nix path, since nix paths are @@ -320,7 +320,7 @@ in dbNumber = lib.mkOption { type = lib.types.int; default = 0; - description = '' + description = lib.mdDoc '' Redis database number. ''; }; @@ -329,7 +329,7 @@ in type = lib.types.bool; default = cfg.redis.host != "localhost"; defaultText = lib.literalExpression ''config.${opt.redis.host} != "localhost"''; - description = '' + description = lib.mdDoc '' Connect to Redis with SSL. ''; }; @@ -342,8 +342,8 @@ in defaultText = lib.literalExpression '' "''${if config.services.discourse.mail.incoming.enable then "notifications" else "noreply"}@''${config.services.discourse.hostname}" ''; - description = '' - The <literal>from:</literal> email address used when + description = lib.mdDoc '' + The `from:` email address used when sending all essential system emails. The domain specified here must have SPF, DKIM and reverse PTR records set correctly for email to arrive. @@ -353,10 +353,10 @@ in contactEmailAddress = lib.mkOption { type = lib.types.str; default = ""; - description = '' + description = lib.mdDoc '' Email address of key contact responsible for this site. Used for critical notifications, as well as on the - <literal>/about</literal> contact form for urgent matters. + `/about` contact form for urgent matters. ''; }; @@ -364,7 +364,7 @@ in serverAddress = lib.mkOption { type = lib.types.str; default = "localhost"; - description = '' + description = lib.mdDoc '' The address of the SMTP server Discourse should use to send email. ''; @@ -373,7 +373,7 @@ in port = lib.mkOption { type = lib.types.port; default = 25; - description = '' + description = lib.mdDoc '' The port of the SMTP server Discourse should use to send email. ''; @@ -382,7 +382,7 @@ in username = lib.mkOption { type = with lib.types; nullOr str; default = null; - description = '' + description = lib.mdDoc '' The username of the SMTP server. ''; }; @@ -390,7 +390,7 @@ in passwordFile = lib.mkOption { type = lib.types.nullOr lib.types.path; default = null; - description = '' + description = lib.mdDoc '' A file containing the password of the SMTP server account. This should be a string, not a nix path, since nix paths @@ -402,7 +402,7 @@ in type = lib.types.str; default = cfg.hostname; defaultText = lib.literalExpression "config.${opt.hostname}"; - description = '' + description = lib.mdDoc '' HELO domain to use for outgoing mail. ''; }; @@ -410,7 +410,7 @@ in authentication = lib.mkOption { type = with lib.types; nullOr (enum ["plain" "login" "cram_md5"]); default = null; - description = '' + description = lib.mdDoc '' Authentication type to use, see http://api.rubyonrails.org/classes/ActionMailer/Base.html ''; }; @@ -418,7 +418,7 @@ in enableStartTLSAuto = lib.mkOption { type = lib.types.bool; default = true; - description = '' + description = lib.mdDoc '' Whether to try to use StartTLS. ''; }; @@ -426,7 +426,7 @@ in opensslVerifyMode = lib.mkOption { type = lib.types.str; default = "peer"; - description = '' + description = lib.mdDoc '' How OpenSSL checks the certificate, see http://api.rubyonrails.org/classes/ActionMailer/Base.html ''; }; @@ -434,7 +434,7 @@ in forceTLS = lib.mkOption { type = lib.types.bool; default = false; - description = '' + description = lib.mdDoc '' Force implicit TLS as per RFC 8314 3.3. ''; }; @@ -444,7 +444,7 @@ in enable = lib.mkOption { type = lib.types.bool; default = false; - description = '' + description = lib.mdDoc '' Whether to set up Postfix to receive incoming mail. ''; }; @@ -453,7 +453,7 @@ in type = lib.types.str; default = "%{reply_key}@${cfg.hostname}"; defaultText = lib.literalExpression ''"%{reply_key}@''${config.services.discourse.hostname}"''; - description = '' + description = lib.mdDoc '' Template for reply by email incoming email address, for example: %{reply_key}@reply.example.com or replies+%{reply_key}@example.com @@ -464,7 +464,7 @@ in type = lib.types.package; default = pkgs.discourse-mail-receiver; defaultText = lib.literalExpression "pkgs.discourse-mail-receiver"; - description = '' + description = lib.mdDoc '' The discourse-mail-receiver package to use. ''; }; @@ -472,10 +472,10 @@ in apiKeyFile = lib.mkOption { type = lib.types.nullOr lib.types.path; default = null; - description = '' + description = lib.mdDoc '' A file containing the Discourse API key used to add posts and messages from mail. If left at its default - value <literal>null</literal>, one will be automatically + value `null`, one will be automatically generated. This should be a string, not a nix path, since nix paths @@ -504,7 +504,7 @@ in sidekiqProcesses = lib.mkOption { type = lib.types.int; default = 1; - description = '' + description = lib.mdDoc '' How many Sidekiq processes should be spawned. ''; }; @@ -512,7 +512,7 @@ in unicornTimeout = lib.mkOption { type = lib.types.int; default = 30; - description = '' + description = lib.mdDoc '' Time in seconds before a request to Unicorn times out. This can be raised if the system Discourse is running on is @@ -604,11 +604,11 @@ in cors_origin = ""; serve_static_assets = false; sidekiq_workers = 5; - rtl_css = false; connection_reaper_age = 30; connection_reaper_interval = 30; relative_url_root = null; message_bus_max_backlog_size = 100; + message_bus_clear_every = 50; secret_key_base = cfg.secretKeyBaseFile; fallback_assets_path = null; @@ -655,7 +655,12 @@ in long_polling_interval = null; }; - services.redis.enable = lib.mkDefault (cfg.redis.host == "localhost"); + services.redis.servers.discourse = + lib.mkIf (lib.elem cfg.redis.host [ "localhost" "127.0.0.1" ]) { + enable = true; + bind = cfg.redis.host; + port = cfg.backendSettings.redis_port; + }; services.postgresql = lib.mkIf databaseActuallyCreateLocally { enable = true; @@ -696,12 +701,12 @@ in systemd.services.discourse = { wantedBy = [ "multi-user.target" ]; after = [ - "redis.service" + "redis-discourse.service" "postgresql.service" "discourse-postgresql.service" ]; bindsTo = [ - "redis.service" + "redis-discourse.service" ] ++ lib.optionals (cfg.database.host == null) [ "postgresql.service" "discourse-postgresql.service" @@ -934,7 +939,6 @@ in proxy_cache discourse; proxy_cache_key "$scheme,$host,$request_uri"; proxy_cache_valid 200 301 302 7d; - proxy_cache_valid any 1m; ''; }; "/message-bus/" = proxy { diff --git a/nixpkgs/nixos/modules/services/web-apps/documize.nix b/nixpkgs/nixos/modules/services/web-apps/documize.nix index 7f2ed82ee33e..4353e3c24453 100644 --- a/nixpkgs/nixos/modules/services/web-apps/documize.nix +++ b/nixpkgs/nixos/modules/services/web-apps/documize.nix @@ -17,8 +17,8 @@ in { stateDirectoryName = mkOption { type = types.str; default = "documize"; - description = '' - The name of the directory below <filename>/var/lib/private</filename> + description = lib.mdDoc '' + The name of the directory below {file}`/var/lib/private` where documize runs in and stores, for example, backups. ''; }; @@ -27,7 +27,7 @@ in { type = types.package; default = pkgs.documize-community; defaultText = literalExpression "pkgs.documize-community"; - description = '' + description = lib.mdDoc '' Which package to use for documize. ''; }; @@ -36,7 +36,7 @@ in { type = types.nullOr types.str; default = null; example = "3edIYV6c8B28b19fh"; - description = '' + description = lib.mdDoc '' The salt string used to encode JWT tokens, if not set a random value will be generated. ''; }; @@ -44,23 +44,23 @@ in { cert = mkOption { type = types.nullOr types.str; default = null; - description = '' - The <filename>cert.pem</filename> file used for https. + description = lib.mdDoc '' + The {file}`cert.pem` file used for https. ''; }; key = mkOption { type = types.nullOr types.str; default = null; - description = '' - The <filename>key.pem</filename> file used for https. + description = lib.mdDoc '' + The {file}`key.pem` file used for https. ''; }; port = mkOption { type = types.port; default = 5001; - description = '' + description = lib.mdDoc '' The http/https port number. ''; }; @@ -68,7 +68,7 @@ in { forcesslport = mkOption { type = types.nullOr types.port; default = null; - description = '' + description = lib.mdDoc '' Redirect given http port number to TLS. ''; }; @@ -76,8 +76,8 @@ in { offline = mkOption { type = types.bool; default = false; - description = '' - Set <literal>true</literal> for offline mode. + description = lib.mdDoc '' + Set `true` for offline mode. ''; apply = v: if true == v then 1 else 0; }; @@ -122,7 +122,7 @@ in { location = mkOption { type = types.nullOr types.str; default = null; - description = '' + description = lib.mdDoc '' reserved ''; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/dokuwiki.nix b/nixpkgs/nixos/modules/services/web-apps/dokuwiki.nix index 1f8ca742db95..a148dec8199a 100644 --- a/nixpkgs/nixos/modules/services/web-apps/dokuwiki.nix +++ b/nixpkgs/nixos/modules/services/web-apps/dokuwiki.nix @@ -66,21 +66,21 @@ let type = types.package; default = pkgs.dokuwiki; defaultText = literalExpression "pkgs.dokuwiki"; - description = "Which DokuWiki package to use."; + description = lib.mdDoc "Which DokuWiki package to use."; }; stateDir = mkOption { type = types.path; default = "/var/lib/dokuwiki/${name}/data"; - description = "Location of the DokuWiki state directory."; + description = lib.mdDoc "Location of the DokuWiki state directory."; }; acl = mkOption { type = types.nullOr types.lines; default = null; example = "* @ALL 8"; - description = '' - Access Control Lists: see <link xlink:href="https://www.dokuwiki.org/acl"/> + description = lib.mdDoc '' + Access Control Lists: see <https://www.dokuwiki.org/acl> Mutually exclusive with services.dokuwiki.aclFile Set this to a value other than null to take precedence over aclFile option. @@ -92,11 +92,11 @@ let aclFile = mkOption { type = with types; nullOr str; default = if (config.aclUse && config.acl == null) then "/var/lib/dokuwiki/${name}/acl.auth.php" else null; - description = '' + description = lib.mdDoc '' Location of the dokuwiki acl rules. Mutually exclusive with services.dokuwiki.acl Mutually exclusive with services.dokuwiki.acl which is preferred. - Consult documentation <link xlink:href="https://www.dokuwiki.org/acl"/> for further instructions. - Example: <link xlink:href="https://github.com/splitbrain/dokuwiki/blob/master/conf/acl.auth.php.dist"/> + Consult documentation <https://www.dokuwiki.org/acl> for further instructions. + Example: <https://github.com/splitbrain/dokuwiki/blob/master/conf/acl.auth.php.dist> ''; example = "/var/lib/dokuwiki/${name}/acl.auth.php"; }; @@ -104,7 +104,7 @@ let aclUse = mkOption { type = types.bool; default = true; - description = '' + description = lib.mdDoc '' Necessary for users to log in into the system. Also limits anonymous users. When disabled, everyone is able to create and edit content. @@ -119,7 +119,7 @@ let $plugins['authmysql'] = 0; $plugins['authpgsql'] = 0; ''; - description = '' + description = lib.mdDoc '' List of the dokuwiki (un)loaded plugins. ''; }; @@ -127,10 +127,10 @@ let superUser = mkOption { type = types.nullOr types.str; default = "@admin"; - description = '' + description = lib.mdDoc '' You can set either a username, a list of usernames (“admin1,admin2”), or the name of a group by prepending an @ char to the groupname - Consult documentation <link xlink:href="https://www.dokuwiki.org/config:superuser"/> for further instructions. + Consult documentation <https://www.dokuwiki.org/config:superuser> for further instructions. ''; }; @@ -150,9 +150,9 @@ let type = types.nullOr types.str; default = ""; example = "search,register"; - description = '' + description = lib.mdDoc '' Disable individual action modes. Refer to - <link xlink:href="https://www.dokuwiki.org/config:action_modes"/> + <https://www.dokuwiki.org/config:action_modes> for details on supported values. ''; }; @@ -222,8 +222,8 @@ let "pm.max_spare_servers" = 4; "pm.max_requests" = 500; }; - description = '' - Options for the DokuWiki PHP pool. See the documentation on <literal>php-fpm.conf</literal> + description = lib.mdDoc '' + Options for the DokuWiki PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives. ''; }; @@ -235,9 +235,9 @@ let $conf['title'] = 'My Wiki'; $conf['userewrite'] = 1; ''; - description = '' + description = lib.mdDoc '' DokuWiki configuration. Refer to - <link xlink:href="https://www.dokuwiki.org/config"/> + <https://www.dokuwiki.org/config> for details on supported values. ''; }; @@ -254,20 +254,20 @@ in sites = mkOption { type = types.attrsOf (types.submodule siteOpts); default = {}; - description = "Specification of one or more DokuWiki sites to serve"; + description = lib.mdDoc "Specification of one or more DokuWiki sites to serve"; }; webserver = mkOption { type = types.enum [ "nginx" "caddy" ]; default = "nginx"; - description = '' + description = lib.mdDoc '' Whether to use nginx or caddy for virtual host management. - Further nginx configuration can be done by adapting <literal>services.nginx.virtualHosts.<name></literal>. - See <xref linkend="opt-services.nginx.virtualHosts"/> for further information. + Further nginx configuration can be done by adapting `services.nginx.virtualHosts.<name>`. + See [](#opt-services.nginx.virtualHosts) for further information. - Further apache2 configuration can be done by adapting <literal>services.httpd.virtualHosts.<name></literal>. - See <xref linkend="opt-services.httpd.virtualHosts"/> for further information. + Further apache2 configuration can be done by adapting `services.httpd.virtualHosts.<name>`. + See [](#opt-services.httpd.virtualHosts) for further information. ''; }; @@ -293,9 +293,7 @@ in inherit user; group = webserver.group; - # Not yet compatible with php 8 https://www.dokuwiki.org/requirements - # https://github.com/splitbrain/dokuwiki/issues/3545 - phpPackage = pkgs.php74; + phpPackage = pkgs.php81; phpEnv = { DOKUWIKI_LOCAL_CONFIG = "${dokuwikiLocalConfig hostName cfg}"; DOKUWIKI_PLUGINS_LOCAL_CONFIG = "${dokuwikiPluginsLocalConfig hostName cfg}"; diff --git a/nixpkgs/nixos/modules/services/web-apps/engelsystem.nix b/nixpkgs/nixos/modules/services/web-apps/engelsystem.nix index 06c3c6dfc3d7..f1d71f174471 100644 --- a/nixpkgs/nixos/modules/services/web-apps/engelsystem.nix +++ b/nixpkgs/nixos/modules/services/web-apps/engelsystem.nix @@ -9,7 +9,7 @@ in { enable = mkOption { default = false; example = true; - description = '' + description = lib.mdDoc '' Whether to enable engelsystem, an online tool for coordinating volunteers and shifts on large events. ''; @@ -19,12 +19,12 @@ in { domain = mkOption { type = types.str; example = "engelsystem.example.com"; - description = "Domain to serve on."; + description = lib.mdDoc "Domain to serve on."; }; package = mkOption { type = types.package; - description = "Engelsystem package used for the service."; + description = lib.mdDoc "Engelsystem package used for the service."; default = pkgs.engelsystem; defaultText = literalExpression "pkgs.engelsystem"; }; @@ -32,9 +32,9 @@ in { createDatabase = mkOption { type = types.bool; default = true; - description = '' + description = lib.mdDoc '' Whether to create a local database automatically. - This will override every database setting in <option>services.engelsystem.config</option>. + This will override every database setting in {option}`services.engelsystem.config`. ''; }; }; @@ -70,7 +70,7 @@ in { min_password_length = 6; default_locale = "de_DE"; }; - description = '' + description = lib.mdDoc '' Options to be added to config.php, as a nix attribute set. Options containing secret data should be set to an attribute set containing the attribute _secret - a string pointing to a file containing the value the option should be set to. See the example to get a better diff --git a/nixpkgs/nixos/modules/services/web-apps/ethercalc.nix b/nixpkgs/nixos/modules/services/web-apps/ethercalc.nix index d74def59c6c3..a5be86a34aa6 100644 --- a/nixpkgs/nixos/modules/services/web-apps/ethercalc.nix +++ b/nixpkgs/nixos/modules/services/web-apps/ethercalc.nix @@ -10,11 +10,11 @@ in { enable = mkOption { default = false; type = types.bool; - description = '' + description = lib.mdDoc '' ethercalc, an online collaborative spreadsheet server. Persistent state will be maintained under - <filename>/var/lib/ethercalc</filename>. Upstream supports using a + {file}`/var/lib/ethercalc`. Upstream supports using a redis server for storage and recommends the redis backend for intensive use; however, the Nix module doesn't currently support redis. @@ -28,19 +28,19 @@ in { default = pkgs.ethercalc; defaultText = literalExpression "pkgs.ethercalc"; type = types.package; - description = "Ethercalc package to use."; + description = lib.mdDoc "Ethercalc package to use."; }; host = mkOption { type = types.str; default = "0.0.0.0"; - description = "Address to listen on (use 0.0.0.0 to allow access from any address)."; + description = lib.mdDoc "Address to listen on (use 0.0.0.0 to allow access from any address)."; }; port = mkOption { type = types.port; default = 8000; - description = "Port to bind to."; + description = lib.mdDoc "Port to bind to."; }; }; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/fluidd.nix b/nixpkgs/nixos/modules/services/web-apps/fluidd.nix index 6ac1acc9d036..8d6d48b3dd27 100644 --- a/nixpkgs/nixos/modules/services/web-apps/fluidd.nix +++ b/nixpkgs/nixos/modules/services/web-apps/fluidd.nix @@ -10,7 +10,7 @@ in package = mkOption { type = types.package; - description = "Fluidd package to be used in the module"; + description = lib.mdDoc "Fluidd package to be used in the module"; default = pkgs.fluidd; defaultText = literalExpression "pkgs.fluidd"; }; @@ -18,7 +18,7 @@ in hostName = mkOption { type = types.str; default = "localhost"; - description = "Hostname to serve fluidd on"; + description = lib.mdDoc "Hostname to serve fluidd on"; }; nginx = mkOption { @@ -30,7 +30,7 @@ in serverAliases = [ "fluidd.''${config.networking.domain}" ]; } ''; - description = "Extra configuration for the nginx virtual host of fluidd."; + description = lib.mdDoc "Extra configuration for the nginx virtual host of fluidd."; }; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/galene.nix b/nixpkgs/nixos/modules/services/web-apps/galene.nix index 1d0a620585b0..2fef43753d79 100644 --- a/nixpkgs/nixos/modules/services/web-apps/galene.nix +++ b/nixpkgs/nixos/modules/services/web-apps/galene.nix @@ -17,7 +17,7 @@ in stateDir = mkOption { default = defaultstateDir; type = types.str; - description = '' + description = lib.mdDoc '' The directory where Galene stores its internal state. If left as the default value this directory will automatically be created before the Galene server starts, otherwise the sysadmin is responsible for ensuring the directory @@ -28,19 +28,19 @@ in user = mkOption { type = types.str; default = "galene"; - description = "User account under which galene runs."; + description = lib.mdDoc "User account under which galene runs."; }; group = mkOption { type = types.str; default = "galene"; - description = "Group under which galene runs."; + description = lib.mdDoc "Group under which galene runs."; }; insecure = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Whether Galene should listen in http or in https. If left as the default value (false), Galene needs to be fed a private key and a certificate. ''; @@ -50,7 +50,7 @@ in type = types.nullOr types.str; default = null; example = "/path/to/your/cert.pem"; - description = '' + description = lib.mdDoc '' Path to the server's certificate. The file is copied at runtime to Galene's data directory where it needs to reside. ''; @@ -60,7 +60,7 @@ in type = types.nullOr types.str; default = null; example = "/path/to/your/key.pem"; - description = '' + description = lib.mdDoc '' Path to the server's private key. The file is copied at runtime to Galene's data directory where it needs to reside. ''; @@ -69,13 +69,13 @@ in httpAddress = mkOption { type = types.str; default = ""; - description = "HTTP listen address for galene."; + description = lib.mdDoc "HTTP listen address for galene."; }; httpPort = mkOption { type = types.port; default = 8443; - description = "HTTP listen port."; + description = lib.mdDoc "HTTP listen port."; }; staticDir = mkOption { @@ -83,7 +83,7 @@ in default = "${cfg.package.static}/static"; defaultText = literalExpression ''"''${package.static}/static"''; example = "/var/lib/galene/static"; - description = "Web server directory."; + description = lib.mdDoc "Web server directory."; }; recordingsDir = mkOption { @@ -91,7 +91,7 @@ in default = defaultrecordingsDir; defaultText = literalExpression ''"''${config.${opt.stateDir}}/recordings"''; example = "/var/lib/galene/recordings"; - description = "Recordings directory."; + description = lib.mdDoc "Recordings directory."; }; dataDir = mkOption { @@ -99,7 +99,7 @@ in default = defaultdataDir; defaultText = literalExpression ''"''${config.${opt.stateDir}}/data"''; example = "/var/lib/galene/data"; - description = "Data directory."; + description = lib.mdDoc "Data directory."; }; groupsDir = mkOption { @@ -107,14 +107,14 @@ in default = defaultgroupsDir; defaultText = literalExpression ''"''${config.${opt.stateDir}}/groups"''; example = "/var/lib/galene/groups"; - description = "Web server directory."; + description = lib.mdDoc "Web server directory."; }; package = mkOption { default = pkgs.galene; defaultText = literalExpression "pkgs.galene"; type = types.package; - description = '' + description = lib.mdDoc '' Package for running Galene. ''; }; @@ -164,6 +164,35 @@ in optional (cfg.dataDir == defaultdataDir) "galene/data" ++ optional (cfg.groupsDir == defaultgroupsDir) "galene/groups" ++ optional (cfg.recordingsDir == defaultrecordingsDir) "galene/recordings"; + + # Hardening + CapabilityBoundingSet = [ "" ]; + DeviceAllow = [ "" ]; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + ReadWritePaths = cfg.recordingsDir; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ]; + UMask = "0077"; } ]; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/gerrit.nix b/nixpkgs/nixos/modules/services/web-apps/gerrit.nix index 6bfc67368dd5..5b36204ff053 100644 --- a/nixpkgs/nixos/modules/services/web-apps/gerrit.nix +++ b/nixpkgs/nixos/modules/services/web-apps/gerrit.nix @@ -65,14 +65,14 @@ in type = types.package; default = pkgs.gerrit; defaultText = literalExpression "pkgs.gerrit"; - description = "Gerrit package to use"; + description = lib.mdDoc "Gerrit package to use"; }; jvmPackage = mkOption { type = types.package; default = pkgs.jre_headless; defaultText = literalExpression "pkgs.jre_headless"; - description = "Java Runtime Environment package to use"; + description = lib.mdDoc "Java Runtime Environment package to use"; }; jvmOpts = mkOption { @@ -81,13 +81,13 @@ in "-Dflogger.backend_factory=com.google.common.flogger.backend.log4j.Log4jBackendFactory#getInstance" "-Dflogger.logging_context=com.google.gerrit.server.logging.LoggingContext#getInstance" ]; - description = "A list of JVM options to start gerrit with."; + description = lib.mdDoc "A list of JVM options to start gerrit with."; }; jvmHeapLimit = mkOption { type = types.str; default = "1024m"; - description = '' + description = lib.mdDoc '' How much memory to allocate to the JVM heap ''; }; @@ -95,8 +95,8 @@ in listenAddress = mkOption { type = types.str; default = "[::]:8080"; - description = '' - <literal>hostname:port</literal> to listen for HTTP traffic. + description = lib.mdDoc '' + `hostname:port` to listen for HTTP traffic. This is bound using the systemd socket activation. ''; @@ -105,25 +105,25 @@ in settings = mkOption { type = gitIniType; default = {}; - description = '' + description = lib.mdDoc '' Gerrit configuration. This will be generated to the - <literal>etc/gerrit.config</literal> file. + `etc/gerrit.config` file. ''; }; replicationSettings = mkOption { type = gitIniType; default = {}; - description = '' + description = lib.mdDoc '' Replication configuration. This will be generated to the - <literal>etc/replication.config</literal> file. + `etc/replication.config` file. ''; }; plugins = mkOption { type = types.listOf types.package; default = []; - description = '' + description = lib.mdDoc '' List of plugins to add to Gerrit. Each derivation is a jar file itself where the name of the derivation is the name of plugin. ''; @@ -132,19 +132,19 @@ in builtinPlugins = mkOption { type = types.listOf (types.enum cfg.package.passthru.plugins); default = []; - description = '' + description = lib.mdDoc '' List of builtins plugins to install. Those are shipped in the - <literal>gerrit.war</literal> file. + `gerrit.war` file. ''; }; serverId = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Set a UUID that uniquely identifies the server. This can be generated with - <literal>nix-shell -p util-linux --run uuidgen</literal>. + `nix-shell -p util-linux --run uuidgen`. ''; }; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/gotify-server.nix b/nixpkgs/nixos/modules/services/web-apps/gotify-server.nix index 03e01f46a944..9e278b41ad15 100644 --- a/nixpkgs/nixos/modules/services/web-apps/gotify-server.nix +++ b/nixpkgs/nixos/modules/services/web-apps/gotify-server.nix @@ -11,7 +11,7 @@ in { port = mkOption { type = types.port; - description = '' + description = lib.mdDoc '' Port the server listens to. ''; }; @@ -19,8 +19,8 @@ in { stateDirectoryName = mkOption { type = types.str; default = "gotify-server"; - description = '' - The name of the directory below <filename>/var/lib</filename> where + description = lib.mdDoc '' + The name of the directory below {file}`/var/lib` where gotify stores its runtime data. ''; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/grocy.nix b/nixpkgs/nixos/modules/services/web-apps/grocy.nix index be2de638dd96..173dd63ddaa1 100644 --- a/nixpkgs/nixos/modules/services/web-apps/grocy.nix +++ b/nixpkgs/nixos/modules/services/web-apps/grocy.nix @@ -10,7 +10,7 @@ in { hostName = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' FQDN for the grocy instance. ''; }; @@ -18,7 +18,7 @@ in { nginx.enableSSL = mkOption { type = types.bool; default = true; - description = '' + description = lib.mdDoc '' Whether or not to enable SSL (with ACME and let's encrypt) for the grocy vhost. ''; @@ -39,7 +39,7 @@ in { "pm.max_requests" = "500"; }; - description = '' + description = lib.mdDoc '' Options for grocy's PHPFPM pool. ''; }; @@ -47,8 +47,8 @@ in { dataDir = mkOption { type = types.str; default = "/var/lib/grocy"; - description = '' - Home directory of the <literal>grocy</literal> user which contains + description = lib.mdDoc '' + Home directory of the `grocy` user which contains the application's state. ''; }; @@ -58,7 +58,7 @@ in { type = types.str; default = "USD"; example = "EUR"; - description = '' + description = lib.mdDoc '' ISO 4217 code for the currency to display. ''; }; @@ -66,7 +66,7 @@ in { culture = mkOption { type = types.enum [ "de" "en" "da" "en_GB" "es" "fr" "hu" "it" "nl" "no" "pl" "pt_BR" "ru" "sk_SK" "sv_SE" "tr" ]; default = "en"; - description = '' + description = lib.mdDoc '' Display language of the frontend. ''; }; @@ -75,14 +75,14 @@ in { showWeekNumber = mkOption { default = true; type = types.bool; - description = '' + description = lib.mdDoc '' Show the number of the weeks in the calendar views. ''; }; firstDayOfWeek = mkOption { default = null; type = types.nullOr (types.enum (range 0 6)); - description = '' + description = lib.mdDoc '' Which day of the week (0=Sunday, 1=Monday etc.) should be the first day. ''; @@ -115,9 +115,9 @@ in { user = "grocy"; group = "nginx"; - # PHP 7.4 is the only version which is supported/tested by upstream: - # https://github.com/grocy/grocy/blob/v3.0.0/README.md#how-to-install - phpPackage = pkgs.php74; + # PHP 8.0 is the only version which is supported/tested by upstream: + # https://github.com/grocy/grocy/blob/v3.3.0/README.md#how-to-install + phpPackage = pkgs.php80; inherit (cfg.phpfpm) settings; diff --git a/nixpkgs/nixos/modules/services/web-apps/healthchecks.nix b/nixpkgs/nixos/modules/services/web-apps/healthchecks.nix new file mode 100644 index 000000000000..e58cc6f202be --- /dev/null +++ b/nixpkgs/nixos/modules/services/web-apps/healthchecks.nix @@ -0,0 +1,249 @@ +{ config, lib, pkgs, buildEnv, ... }: + +with lib; + +let + defaultUser = "healthchecks"; + cfg = config.services.healthchecks; + pkg = cfg.package; + boolToPython = b: if b then "True" else "False"; + environment = { + PYTHONPATH = pkg.pythonPath; + STATIC_ROOT = cfg.dataDir + "/static"; + DB_NAME = "${cfg.dataDir}/healthchecks.sqlite"; + } // cfg.settings; + + environmentFile = pkgs.writeText "healthchecks-environment" (lib.generators.toKeyValue { } environment); + + healthchecksManageScript = with pkgs; (writeShellScriptBin "healthchecks-manage" '' + if [[ "$USER" != "${cfg.user}" ]]; then + echo "please run as user 'healtchecks'." >/dev/stderr + exit 1 + fi + export $(cat ${environmentFile} | xargs); + exec ${pkg}/opt/healthchecks/manage.py "$@" + ''); +in +{ + options.services.healthchecks = { + enable = mkEnableOption "healthchecks" // { + description = '' + Enable healthchecks. + It is expected to be run behind a HTTP reverse proxy. + ''; + }; + + package = mkOption { + default = pkgs.healthchecks; + defaultText = literalExpression "pkgs.healthchecks"; + type = types.package; + description = lib.mdDoc "healthchecks package to use."; + }; + + user = mkOption { + default = defaultUser; + type = types.str; + description = '' + User account under which healthchecks runs. + + <note><para> + If left as the default value this user will automatically be created + on system activation, otherwise you are responsible for + ensuring the user exists before the healthchecks service starts. + </para></note> + ''; + }; + + group = mkOption { + default = defaultUser; + type = types.str; + description = '' + Group account under which healthchecks runs. + + <note><para> + If left as the default value this group will automatically be created + on system activation, otherwise you are responsible for + ensuring the group exists before the healthchecks service starts. + </para></note> + ''; + }; + + listenAddress = mkOption { + type = types.str; + default = "localhost"; + description = lib.mdDoc "Address the server will listen on."; + }; + + port = mkOption { + type = types.port; + default = 8000; + description = lib.mdDoc "Port the server will listen on."; + }; + + dataDir = mkOption { + type = types.str; + default = "/var/lib/healthchecks"; + description = '' + The directory used to store all data for healthchecks. + + <note><para> + If left as the default value this directory will automatically be created before + the healthchecks server starts, otherwise you are responsible for ensuring the + directory exists with appropriate ownership and permissions. + </para></note> + ''; + }; + + settings = lib.mkOption { + description = '' + Environment variables which are read by healthchecks <literal>(local)_settings.py</literal>. + + Settings which are explictly covered in options bewlow, are type-checked and/or transformed + before added to the environment, everything else is passed as a string. + + See <link xlink:href="">https://healthchecks.io/docs/self_hosted_configuration/</link> + for a full documentation of settings. + + We add two variables to this list inside the packages <literal>local_settings.py.</literal> + - STATIC_ROOT to set a state directory for dynamically generated static files. + - SECRET_KEY_FILE to read SECRET_KEY from a file at runtime and keep it out of /nix/store. + ''; + type = types.submodule { + freeformType = types.attrsOf types.str; + options = { + ALLOWED_HOSTS = lib.mkOption { + type = types.listOf types.str; + default = [ "*" ]; + description = lib.mdDoc "The host/domain names that this site can serve."; + apply = lib.concatStringsSep ","; + }; + + SECRET_KEY_FILE = mkOption { + type = types.path; + description = lib.mdDoc "Path to a file containing the secret key."; + }; + + DEBUG = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Enable debug mode."; + apply = boolToPython; + }; + + REGISTRATION_OPEN = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + A boolean that controls whether site visitors can create new accounts. + Set it to false if you are setting up a private Healthchecks instance, + but it needs to be publicly accessible (so, for example, your cloud + services can send pings to it). + If you close new user registration, you can still selectively invite + users to your team account. + ''; + apply = boolToPython; + }; + }; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ healthchecksManageScript ]; + + systemd.targets.healthchecks = { + description = "Target for all Healthchecks services"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" "network-online.target" ]; + }; + + systemd.services = + let + commonConfig = { + WorkingDirectory = cfg.dataDir; + User = cfg.user; + Group = cfg.group; + EnvironmentFile = environmentFile; + StateDirectory = mkIf (cfg.dataDir == "/var/lib/healthchecks") "healthchecks"; + StateDirectoryMode = mkIf (cfg.dataDir == "/var/lib/healthchecks") "0750"; + }; + in + { + healthchecks-migration = { + description = "Healthchecks migrations"; + wantedBy = [ "healthchecks.target" ]; + + serviceConfig = commonConfig // { + Restart = "on-failure"; + Type = "oneshot"; + ExecStart = '' + ${pkg}/opt/healthchecks/manage.py migrate + ''; + }; + }; + + healthchecks = { + description = "Healthchecks WSGI Service"; + wantedBy = [ "healthchecks.target" ]; + after = [ "healthchecks-migration.service" ]; + + preStart = '' + ${pkg}/opt/healthchecks/manage.py collectstatic --no-input + ${pkg}/opt/healthchecks/manage.py remove_stale_contenttypes --no-input + ${pkg}/opt/healthchecks/manage.py compress + ''; + + serviceConfig = commonConfig // { + Restart = "always"; + ExecStart = '' + ${pkgs.python3Packages.gunicorn}/bin/gunicorn hc.wsgi \ + --bind ${cfg.listenAddress}:${toString cfg.port} \ + --pythonpath ${pkg}/opt/healthchecks + ''; + }; + }; + + healthchecks-sendalerts = { + description = "Healthchecks Alert Service"; + wantedBy = [ "healthchecks.target" ]; + after = [ "healthchecks.service" ]; + + serviceConfig = commonConfig // { + Restart = "always"; + ExecStart = '' + ${pkg}/opt/healthchecks/manage.py sendalerts + ''; + }; + }; + + healthchecks-sendreports = { + description = "Healthchecks Reporting Service"; + wantedBy = [ "healthchecks.target" ]; + after = [ "healthchecks.service" ]; + + serviceConfig = commonConfig // { + Restart = "always"; + ExecStart = '' + ${pkg}/opt/healthchecks/manage.py sendreports --loop + ''; + }; + }; + }; + + users.users = optionalAttrs (cfg.user == defaultUser) { + ${defaultUser} = + { + description = "healthchecks service owner"; + isSystemUser = true; + group = defaultUser; + }; + }; + + users.groups = optionalAttrs (cfg.user == defaultUser) { + ${defaultUser} = + { + members = [ defaultUser ]; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/web-apps/hedgedoc.nix b/nixpkgs/nixos/modules/services/web-apps/hedgedoc.nix index 9eeabb9d5662..348192ea8486 100644 --- a/nixpkgs/nixos/modules/services/web-apps/hedgedoc.nix +++ b/nixpkgs/nixos/modules/services/web-apps/hedgedoc.nix @@ -13,17 +13,22 @@ let then "hedgedoc" else "codimd"; + settingsFormat = pkgs.formats.json {}; + prettyJSON = conf: pkgs.runCommandLocal "hedgedoc-config.json" { nativeBuildInputs = [ pkgs.jq ]; } '' - echo '${builtins.toJSON conf}' | jq \ - '{production:del(.[]|nulls)|del(.[][]?|nulls)}' > $out + jq '{production:del(.[]|nulls)|del(.[][]?|nulls)}' \ + < ${settingsFormat.generate "hedgedoc-ugly.json" cfg.settings} \ + > $out ''; in { imports = [ (mkRenamedOptionModule [ "services" "codimd" ] [ "services" "hedgedoc" ]) + (mkRenamedOptionModule + [ "services" "hedgedoc" "configuration" ] [ "services" "hedgedoc" "settings" ]) ]; options.services.hedgedoc = { @@ -32,7 +37,7 @@ in groups = mkOption { type = types.listOf types.str; default = []; - description = '' + description = lib.mdDoc '' Groups to which the service user should be added. ''; }; @@ -40,18 +45,18 @@ in workDir = mkOption { type = types.path; default = "/var/lib/${name}"; - description = '' + description = lib.mdDoc '' Working directory for the HedgeDoc service. ''; }; - configuration = { + settings = let options = { debug = mkEnableOption "debug mode"; domain = mkOption { type = types.nullOr types.str; default = null; example = "hedgedoc.org"; - description = '' + description = lib.mdDoc '' Domain name for the HedgeDoc instance. ''; }; @@ -59,14 +64,14 @@ in type = types.nullOr types.str; default = null; example = "/url/path/to/hedgedoc"; - description = '' + description = lib.mdDoc '' Path under which HedgeDoc is accessible. ''; }; host = mkOption { type = types.str; default = "localhost"; - description = '' + description = lib.mdDoc '' Address to listen on. ''; }; @@ -74,7 +79,7 @@ in type = types.int; default = 3000; example = 80; - description = '' + description = lib.mdDoc '' Port to listen on. ''; }; @@ -82,7 +87,7 @@ in type = types.nullOr types.str; default = null; example = "/run/hedgedoc.sock"; - description = '' + description = lib.mdDoc '' Specify where a UNIX domain socket should be placed. ''; }; @@ -90,44 +95,44 @@ in type = types.listOf types.str; default = []; example = [ "localhost" "hedgedoc.org" ]; - description = '' + description = lib.mdDoc '' List of domains to whitelist. ''; }; useSSL = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Enable to use SSL server. This will also enable - <option>protocolUseSSL</option>. + {option}`protocolUseSSL`. ''; }; hsts = { enable = mkOption { type = types.bool; default = true; - description = '' + description = lib.mdDoc '' Whether to enable HSTS if HTTPS is also enabled. ''; }; maxAgeSeconds = mkOption { type = types.int; default = 31536000; - description = '' + description = lib.mdDoc '' Max duration for clients to keep the HSTS status. ''; }; includeSubdomains = mkOption { type = types.bool; default = true; - description = '' + description = lib.mdDoc '' Whether to include subdomains in HSTS. ''; }; preload = mkOption { type = types.bool; default = true; - description = '' + description = lib.mdDoc '' Whether to allow preloading of the site's HSTS status. ''; }; @@ -145,40 +150,39 @@ in addDefaults = true; } ''; - description = '' + description = lib.mdDoc '' Specify the Content Security Policy which is passed to Helmet. - For configuration details see <link xlink:href="https://helmetjs.github.io/docs/csp/" - >https://helmetjs.github.io/docs/csp/</link>. + For configuration details see <https://helmetjs.github.io/docs/csp/>. ''; }; protocolUseSSL = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Enable to use TLS for resource paths. - This only applies when <option>domain</option> is set. + This only applies when {option}`domain` is set. ''; }; urlAddPort = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Enable to add the port to callback URLs. - This only applies when <option>domain</option> is set + This only applies when {option}`domain` is set and only for ports other than 80 and 443. ''; }; useCDN = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Whether to use CDN resources or not. ''; }; allowAnonymous = mkOption { type = types.bool; default = true; - description = '' + description = lib.mdDoc '' Whether to allow anonymous usage. ''; }; @@ -193,14 +197,21 @@ in allowFreeURL = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Whether to allow note creation by accessing a nonexistent note URL. ''; }; + requireFreeURLAuthentication = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Whether to require authentication for FreeURL mode style note creation. + ''; + }; defaultPermission = mkOption { type = types.enum [ "freely" "editable" "limited" "locked" "private" ]; default = "editable"; - description = '' + description = lib.mdDoc '' Default permissions for notes. This only applies for signed-in users. ''; @@ -211,12 +222,12 @@ in example = '' postgres://user:pass@host:5432/dbname ''; - description = '' + description = lib.mdDoc '' Specify which database to use. HedgeDoc supports mysql, postgres, sqlite and mssql. - See <link xlink:href="https://sequelize.readthedocs.io/en/v3/"> - https://sequelize.readthedocs.io/en/v3/</link> for more information. - Note: This option overrides <option>db</option>. + See [ + https://sequelize.readthedocs.io/en/v3/](https://sequelize.readthedocs.io/en/v3/) for more information. + Note: This option overrides {option}`db`. ''; }; db = mkOption { @@ -228,52 +239,52 @@ in storage = "/var/lib/${name}/db.${name}.sqlite"; } ''; - description = '' + description = lib.mdDoc '' Specify the configuration for sequelize. HedgeDoc supports mysql, postgres, sqlite and mssql. - See <link xlink:href="https://sequelize.readthedocs.io/en/v3/"> - https://sequelize.readthedocs.io/en/v3/</link> for more information. - Note: This option overrides <option>db</option>. + See [ + https://sequelize.readthedocs.io/en/v3/](https://sequelize.readthedocs.io/en/v3/) for more information. + Note: This option overrides {option}`db`. ''; }; sslKeyPath= mkOption { type = types.nullOr types.str; default = null; example = "/var/lib/hedgedoc/hedgedoc.key"; - description = '' - Path to the SSL key. Needed when <option>useSSL</option> is enabled. + description = lib.mdDoc '' + Path to the SSL key. Needed when {option}`useSSL` is enabled. ''; }; sslCertPath = mkOption { type = types.nullOr types.str; default = null; example = "/var/lib/hedgedoc/hedgedoc.crt"; - description = '' - Path to the SSL cert. Needed when <option>useSSL</option> is enabled. + description = lib.mdDoc '' + Path to the SSL cert. Needed when {option}`useSSL` is enabled. ''; }; sslCAPath = mkOption { type = types.listOf types.str; default = []; example = [ "/var/lib/hedgedoc/ca.crt" ]; - description = '' - SSL ca chain. Needed when <option>useSSL</option> is enabled. + description = lib.mdDoc '' + SSL ca chain. Needed when {option}`useSSL` is enabled. ''; }; dhParamPath = mkOption { type = types.nullOr types.str; default = null; example = "/var/lib/hedgedoc/dhparam.pem"; - description = '' - Path to the SSL dh params. Needed when <option>useSSL</option> is enabled. + description = lib.mdDoc '' + Path to the SSL dh params. Needed when {option}`useSSL` is enabled. ''; }; tmpPath = mkOption { type = types.str; default = "/tmp"; - description = '' + description = lib.mdDoc '' Path to the temp directory HedgeDoc should use. - Note that <option>serviceConfig.PrivateTmp</option> is enabled for + Note that {option}`serviceConfig.PrivateTmp` is enabled for the HedgeDoc systemd service by default. (Non-canonical paths are relative to HedgeDoc's base directory) ''; @@ -281,7 +292,7 @@ in defaultNotePath = mkOption { type = types.nullOr types.str; default = "./public/default.md"; - description = '' + description = lib.mdDoc '' Path to the default Note file. (Non-canonical paths are relative to HedgeDoc's base directory) ''; @@ -289,7 +300,7 @@ in docsPath = mkOption { type = types.nullOr types.str; default = "./public/docs"; - description = '' + description = lib.mdDoc '' Path to the docs directory. (Non-canonical paths are relative to HedgeDoc's base directory) ''; @@ -297,7 +308,7 @@ in indexPath = mkOption { type = types.nullOr types.str; default = "./public/views/index.ejs"; - description = '' + description = lib.mdDoc '' Path to the index template file. (Non-canonical paths are relative to HedgeDoc's base directory) ''; @@ -305,7 +316,7 @@ in hackmdPath = mkOption { type = types.nullOr types.str; default = "./public/views/hackmd.ejs"; - description = '' + description = lib.mdDoc '' Path to the hackmd template file. (Non-canonical paths are relative to HedgeDoc's base directory) ''; @@ -314,7 +325,7 @@ in type = types.nullOr types.str; default = null; defaultText = literalExpression "./public/views/error.ejs"; - description = '' + description = lib.mdDoc '' Path to the error template file. (Non-canonical paths are relative to HedgeDoc's base directory) ''; @@ -323,7 +334,7 @@ in type = types.nullOr types.str; default = null; defaultText = literalExpression "./public/views/pretty.ejs"; - description = '' + description = lib.mdDoc '' Path to the pretty template file. (Non-canonical paths are relative to HedgeDoc's base directory) ''; @@ -332,7 +343,7 @@ in type = types.nullOr types.str; default = null; defaultText = literalExpression "./public/views/slide.hbs"; - description = '' + description = lib.mdDoc '' Path to the slide template file. (Non-canonical paths are relative to HedgeDoc's base directory) ''; @@ -341,21 +352,21 @@ in type = types.str; default = "${cfg.workDir}/uploads"; defaultText = literalExpression "/var/lib/${name}/uploads"; - description = '' + description = lib.mdDoc '' Path under which uploaded files are saved. ''; }; sessionName = mkOption { type = types.str; default = "connect.sid"; - description = '' + description = lib.mdDoc '' Specify the name of the session cookie. ''; }; sessionSecret = mkOption { type = types.nullOr types.str; default = null; - description = '' + description = lib.mdDoc '' Specify the secret used to sign the session cookie. If unset, one will be generated on startup. ''; @@ -363,56 +374,56 @@ in sessionLife = mkOption { type = types.int; default = 1209600000; - description = '' + description = lib.mdDoc '' Session life time in milliseconds. ''; }; heartbeatInterval = mkOption { type = types.int; default = 5000; - description = '' + description = lib.mdDoc '' Specify the socket.io heartbeat interval. ''; }; heartbeatTimeout = mkOption { type = types.int; default = 10000; - description = '' + description = lib.mdDoc '' Specify the socket.io heartbeat timeout. ''; }; documentMaxLength = mkOption { type = types.int; default = 100000; - description = '' + description = lib.mdDoc '' Specify the maximum document length. ''; }; email = mkOption { type = types.bool; default = true; - description = '' + description = lib.mdDoc '' Whether to enable email sign-in. ''; }; allowEmailRegister = mkOption { type = types.bool; default = true; - description = '' + description = lib.mdDoc '' Whether to enable email registration. ''; }; allowGravatar = mkOption { type = types.bool; default = true; - description = '' + description = lib.mdDoc '' Whether to use gravatar as profile picture source. ''; }; imageUploadType = mkOption { type = types.enum [ "imgur" "s3" "minio" "filesystem" ]; default = "filesystem"; - description = '' + description = lib.mdDoc '' Specify where to upload images. ''; }; @@ -421,85 +432,85 @@ in options = { accessKey = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Minio access key. ''; }; secretKey = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Minio secret key. ''; }; - endpoint = mkOption { + endPoint = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Minio endpoint. ''; }; port = mkOption { type = types.int; default = 9000; - description = '' + description = lib.mdDoc '' Minio listen port. ''; }; secure = mkOption { type = types.bool; default = true; - description = '' + description = lib.mdDoc '' Whether to use HTTPS for Minio. ''; }; }; }); default = null; - description = "Configure the minio third-party integration."; + description = lib.mdDoc "Configure the minio third-party integration."; }; s3 = mkOption { type = types.nullOr (types.submodule { options = { accessKeyId = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' AWS access key id. ''; }; secretAccessKey = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' AWS access key. ''; }; region = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' AWS S3 region. ''; }; }; }); default = null; - description = "Configure the s3 third-party integration."; + description = lib.mdDoc "Configure the s3 third-party integration."; }; s3bucket = mkOption { type = types.nullOr types.str; default = null; - description = '' - Specify the bucket name for upload types <literal>s3</literal> and <literal>minio</literal>. + description = lib.mdDoc '' + Specify the bucket name for upload types `s3` and `minio`. ''; }; allowPDFExport = mkOption { type = types.bool; default = true; - description = '' + description = lib.mdDoc '' Whether to enable PDF exports. ''; }; imgur.clientId = mkOption { type = types.nullOr types.str; default = null; - description = '' + description = lib.mdDoc '' Imgur API client ID. ''; }; @@ -508,13 +519,13 @@ in options = { connectionString = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Azure Blob Storage connection string. ''; }; container = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Azure Blob Storage container name. It will be created if non-existent. ''; @@ -522,162 +533,162 @@ in }; }); default = null; - description = "Configure the azure third-party integration."; + description = lib.mdDoc "Configure the azure third-party integration."; }; oauth2 = mkOption { type = types.nullOr (types.submodule { options = { authorizationURL = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Specify the OAuth authorization URL. ''; }; tokenURL = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Specify the OAuth token URL. ''; }; baseURL = mkOption { type = with types; nullOr str; default = null; - description = '' + description = lib.mdDoc '' Specify the OAuth base URL. ''; }; userProfileURL = mkOption { type = with types; nullOr str; default = null; - description = '' + description = lib.mdDoc '' Specify the OAuth userprofile URL. ''; }; userProfileUsernameAttr = mkOption { type = with types; nullOr str; default = null; - description = '' + description = lib.mdDoc '' Specify the name of the attribute for the username from the claim. ''; }; userProfileDisplayNameAttr = mkOption { type = with types; nullOr str; default = null; - description = '' + description = lib.mdDoc '' Specify the name of the attribute for the display name from the claim. ''; }; userProfileEmailAttr = mkOption { type = with types; nullOr str; default = null; - description = '' + description = lib.mdDoc '' Specify the name of the attribute for the email from the claim. ''; }; scope = mkOption { type = with types; nullOr str; default = null; - description = '' + description = lib.mdDoc '' Specify the OAuth scope. ''; }; providerName = mkOption { type = with types; nullOr str; default = null; - description = '' + description = lib.mdDoc '' Specify the name to be displayed for this strategy. ''; }; rolesClaim = mkOption { type = with types; nullOr str; default = null; - description = '' + description = lib.mdDoc '' Specify the role claim name. ''; }; accessRole = mkOption { type = with types; nullOr str; default = null; - description = '' + description = lib.mdDoc '' Specify role which should be included in the ID token roles claim to grant access ''; }; clientID = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Specify the OAuth client ID. ''; }; clientSecret = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Specify the OAuth client secret. ''; }; }; }); default = null; - description = "Configure the OAuth integration."; + description = lib.mdDoc "Configure the OAuth integration."; }; facebook = mkOption { type = types.nullOr (types.submodule { options = { clientID = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Facebook API client ID. ''; }; clientSecret = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Facebook API client secret. ''; }; }; }); default = null; - description = "Configure the facebook third-party integration"; + description = lib.mdDoc "Configure the facebook third-party integration"; }; twitter = mkOption { type = types.nullOr (types.submodule { options = { consumerKey = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Twitter API consumer key. ''; }; consumerSecret = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Twitter API consumer secret. ''; }; }; }); default = null; - description = "Configure the Twitter third-party integration."; + description = lib.mdDoc "Configure the Twitter third-party integration."; }; github = mkOption { type = types.nullOr (types.submodule { options = { clientID = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' GitHub API client ID. ''; }; clientSecret = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Github API client secret. ''; }; }; }); default = null; - description = "Configure the GitHub third-party integration."; + description = lib.mdDoc "Configure the GitHub third-party integration."; }; gitlab = mkOption { type = types.nullOr (types.submodule { @@ -685,27 +696,27 @@ in baseURL = mkOption { type = types.str; default = ""; - description = '' + description = lib.mdDoc '' GitLab API authentication endpoint. Only needed for other endpoints than gitlab.com. ''; }; clientID = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' GitLab API client ID. ''; }; clientSecret = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' GitLab API client secret. ''; }; scope = mkOption { type = types.enum [ "api" "read_user" ]; default = "api"; - description = '' + description = lib.mdDoc '' GitLab API requested scope. GitLab snippet import/export requires api scope. ''; @@ -713,79 +724,79 @@ in }; }); default = null; - description = "Configure the GitLab third-party integration."; + description = lib.mdDoc "Configure the GitLab third-party integration."; }; mattermost = mkOption { type = types.nullOr (types.submodule { options = { baseURL = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Mattermost authentication endpoint. ''; }; clientID = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Mattermost API client ID. ''; }; clientSecret = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Mattermost API client secret. ''; }; }; }); default = null; - description = "Configure the Mattermost third-party integration."; + description = lib.mdDoc "Configure the Mattermost third-party integration."; }; dropbox = mkOption { type = types.nullOr (types.submodule { options = { clientID = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Dropbox API client ID. ''; }; clientSecret = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Dropbox API client secret. ''; }; appKey = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Dropbox app key. ''; }; }; }); default = null; - description = "Configure the Dropbox third-party integration."; + description = lib.mdDoc "Configure the Dropbox third-party integration."; }; google = mkOption { type = types.nullOr (types.submodule { options = { clientID = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Google API client ID. ''; }; clientSecret = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Google API client secret. ''; }; }; }); default = null; - description = "Configure the Google third-party integration."; + description = lib.mdDoc "Configure the Google third-party integration."; }; ldap = mkOption { type = types.nullOr (types.submodule { @@ -793,76 +804,78 @@ in providerName = mkOption { type = types.str; default = ""; - description = '' + description = lib.mdDoc '' Optional name to be displayed at login form, indicating the LDAP provider. ''; }; url = mkOption { type = types.str; example = "ldap://localhost"; - description = '' + description = lib.mdDoc '' URL of LDAP server. ''; }; bindDn = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Bind DN for LDAP access. ''; }; bindCredentials = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Bind credentials for LDAP access. ''; }; searchBase = mkOption { type = types.str; example = "o=users,dc=example,dc=com"; - description = '' + description = lib.mdDoc '' LDAP directory to begin search from. ''; }; searchFilter = mkOption { type = types.str; example = "(uid={{username}})"; - description = '' + description = lib.mdDoc '' LDAP filter to search with. ''; }; searchAttributes = mkOption { - type = types.listOf types.str; + type = types.nullOr (types.listOf types.str); + default = null; example = [ "displayName" "mail" ]; - description = '' + description = lib.mdDoc '' LDAP attributes to search with. ''; }; userNameField = mkOption { type = types.str; default = ""; - description = '' + description = lib.mdDoc '' LDAP field which is used as the username on HedgeDoc. - By default <option>useridField</option> is used. + By default {option}`useridField` is used. ''; }; useridField = mkOption { type = types.str; example = "uid"; - description = '' + description = lib.mdDoc '' LDAP field which is a unique identifier for users on HedgeDoc. ''; }; tlsca = mkOption { type = types.str; + default = "/etc/ssl/certs/ca-certificates.crt"; example = "server-cert.pem,root.pem"; - description = '' + description = lib.mdDoc '' Root CA for LDAP TLS in PEM format. ''; }; }; }); default = null; - description = "Configure the LDAP integration."; + description = lib.mdDoc "Configure the LDAP integration."; }; saml = mkOption { type = types.nullOr (types.submodule { @@ -870,21 +883,21 @@ in idpSsoUrl = mkOption { type = types.str; example = "https://idp.example.com/sso"; - description = '' + description = lib.mdDoc '' IdP authentication endpoint. ''; }; idpCert = mkOption { type = types.path; example = "/path/to/cert.pem"; - description = '' + description = lib.mdDoc '' Path to IdP certificate file in PEM format. ''; }; issuer = mkOption { type = types.str; default = ""; - description = '' + description = lib.mdDoc '' Optional identity of the service provider. This defaults to the server URL. ''; @@ -892,7 +905,7 @@ in identifierFormat = mkOption { type = types.str; default = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"; - description = '' + description = lib.mdDoc '' Optional name identifier format. ''; }; @@ -900,7 +913,7 @@ in type = types.str; default = ""; example = "memberOf"; - description = '' + description = lib.mdDoc '' Optional attribute name for group list. ''; }; @@ -908,7 +921,7 @@ in type = types.listOf types.str; default = []; example = [ "Temporary-staff" "External-users" ]; - description = '' + description = lib.mdDoc '' Excluded group names. ''; }; @@ -916,7 +929,7 @@ in type = types.listOf types.str; default = []; example = [ "Hedgedoc-Users" ]; - description = '' + description = lib.mdDoc '' Required group names. ''; }; @@ -951,8 +964,18 @@ in }; }); default = null; - description = "Configure the SAML integration."; + description = lib.mdDoc "Configure the SAML integration."; + }; + }; in lib.mkOption { + type = lib.types.submodule { + freeformType = settingsFormat.type; + inherit options; }; + description = lib.mdDoc '' + HedgeDoc configuration, see + <https://docs.hedgedoc.org/configuration/> + for documentation. + ''; }; environmentFile = mkOption { @@ -960,9 +983,7 @@ in default = null; example = "/var/lib/hedgedoc/hedgedoc.env"; description = '' - Environment file as defined in <citerefentry> - <refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum> - </citerefentry>. + Environment file as defined in <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>. Secrets may be passed to the service without adding them to the world-readable Nix store, by specifying placeholder variables as the option value in Nix and @@ -989,16 +1010,17 @@ in type = types.package; default = pkgs.hedgedoc; defaultText = literalExpression "pkgs.hedgedoc"; - description = '' + description = lib.mdDoc '' Package that provides HedgeDoc. ''; }; + }; config = mkIf cfg.enable { assertions = [ - { assertion = cfg.configuration.db == {} -> ( - cfg.configuration.dbURL != "" && cfg.configuration.dbURL != null + { assertion = cfg.settings.db == {} -> ( + cfg.settings.dbURL != "" && cfg.settings.dbURL != null ); message = "Database configuration for HedgeDoc missing."; } ]; @@ -1019,10 +1041,12 @@ in preStart = '' ${pkgs.envsubst}/bin/envsubst \ -o ${cfg.workDir}/config.json \ - -i ${prettyJSON cfg.configuration} + -i ${prettyJSON cfg.settings} + mkdir -p ${cfg.settings.uploadsPath} ''; serviceConfig = { WorkingDirectory = cfg.workDir; + StateDirectory = [ cfg.workDir cfg.settings.uploadsPath ]; ExecStart = "${cfg.package}/bin/hedgedoc"; EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ]; Environment = [ diff --git a/nixpkgs/nixos/modules/services/web-apps/hledger-web.nix b/nixpkgs/nixos/modules/services/web-apps/hledger-web.nix index 4f6a34e6d2fe..4f02a637cdd8 100644 --- a/nixpkgs/nixos/modules/services/web-apps/hledger-web.nix +++ b/nixpkgs/nixos/modules/services/web-apps/hledger-web.nix @@ -12,7 +12,7 @@ in { host = mkOption { type = types.str; default = "127.0.0.1"; - description = '' + description = lib.mdDoc '' Address to listen on. ''; }; @@ -21,7 +21,7 @@ in { type = types.port; default = 5000; example = 80; - description = '' + description = lib.mdDoc '' Port to listen on. ''; }; @@ -30,21 +30,21 @@ in { view = mkOption { type = types.bool; default = true; - description = '' + description = lib.mdDoc '' Enable the view capability. ''; }; add = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Enable the add capability. ''; }; manage = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Enable the manage capability. ''; }; @@ -53,7 +53,7 @@ in { stateDir = mkOption { type = types.path; default = "/var/lib/hledger-web"; - description = '' + description = lib.mdDoc '' Path the service has access to. If left as the default value this directory will automatically be created before the hledger-web server starts, otherwise the sysadmin is responsible for ensuring the @@ -64,8 +64,8 @@ in { journalFiles = mkOption { type = types.listOf types.str; default = [ ".hledger.journal" ]; - description = '' - Paths to journal files relative to <option>services.hledger-web.stateDir</option>. + description = lib.mdDoc '' + Paths to journal files relative to {option}`services.hledger-web.stateDir`. ''; }; @@ -73,7 +73,7 @@ in { type = with types; nullOr str; default = null; example = "https://example.org"; - description = '' + description = lib.mdDoc '' Base URL, when sharing over a network. ''; }; @@ -82,7 +82,7 @@ in { type = types.listOf types.str; default = []; example = [ "--forecast" ]; - description = '' + description = lib.mdDoc '' Extra command line arguments to pass to hledger-web. ''; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/icingaweb2/icingaweb2.nix b/nixpkgs/nixos/modules/services/web-apps/icingaweb2/icingaweb2.nix index b9761061aaae..b96baaec7678 100644 --- a/nixpkgs/nixos/modules/services/web-apps/icingaweb2/icingaweb2.nix +++ b/nixpkgs/nixos/modules/services/web-apps/icingaweb2/icingaweb2.nix @@ -17,7 +17,7 @@ in { pool = mkOption { type = str; default = poolName; - description = '' + description = lib.mdDoc '' Name of existing PHP-FPM pool that is used to run Icingaweb2. If not specified, a pool will automatically created with default values. ''; @@ -26,7 +26,7 @@ in { libraryPaths = mkOption { type = attrsOf package; default = { }; - description = '' + description = lib.mdDoc '' Libraries to add to the Icingaweb2 library path. The name of the attribute is the name of the library, the value is the package to add. @@ -36,7 +36,7 @@ in { virtualHost = mkOption { type = nullOr str; default = "icingaweb2"; - description = '' + description = lib.mdDoc '' Name of the nginx virtualhost to use and setup. If null, no virtualhost is set up. ''; }; @@ -45,7 +45,7 @@ in { type = str; default = "UTC"; example = "Europe/Berlin"; - description = "PHP-compliant timezone specification"; + description = lib.mdDoc "PHP-compliant timezone specification"; }; modules = { @@ -64,7 +64,7 @@ in { "snow" = icingaweb2Modules.theme-snow; } ''; - description = '' + description = lib.mdDoc '' Name-package attrset of Icingaweb 2 modules packages to enable. If you enable modules manually (e.g. via the web ui), they will not be touched. @@ -84,7 +84,7 @@ in { level = "CRITICAL"; }; }; - description = '' + description = lib.mdDoc '' config.ini contents. Will automatically be converted to a .ini file. If you don't set global.module_path, the module will take care of it. @@ -108,7 +108,7 @@ in { dbname = "icingaweb2"; }; }; - description = '' + description = lib.mdDoc '' resources.ini contents. Will automatically be converted to a .ini file. @@ -127,7 +127,7 @@ in { resource = "icingaweb_db"; }; }; - description = '' + description = lib.mdDoc '' authentication.ini contents. Will automatically be converted to a .ini file. @@ -145,7 +145,7 @@ in { resource = "icingaweb_db"; }; }; - description = '' + description = lib.mdDoc '' groups.ini contents. Will automatically be converted to a .ini file. @@ -163,7 +163,7 @@ in { permissions = "*"; }; }; - description = '' + description = lib.mdDoc '' roles.ini contents. Will automatically be converted to a .ini file. diff --git a/nixpkgs/nixos/modules/services/web-apps/icingaweb2/module-monitoring.nix b/nixpkgs/nixos/modules/services/web-apps/icingaweb2/module-monitoring.nix index e9c1d4ffe5ea..0579c602216d 100644 --- a/nixpkgs/nixos/modules/services/web-apps/icingaweb2/module-monitoring.nix +++ b/nixpkgs/nixos/modules/services/web-apps/icingaweb2/module-monitoring.nix @@ -34,32 +34,32 @@ in { enable = mkOption { type = bool; default = true; - description = "Whether to enable the icingaweb2 monitoring module."; + description = lib.mdDoc "Whether to enable the icingaweb2 monitoring module."; }; generalConfig = { mutable = mkOption { type = bool; default = false; - description = "Make config.ini of the monitoring module mutable (e.g. via the web interface)."; + description = lib.mdDoc "Make config.ini of the monitoring module mutable (e.g. via the web interface)."; }; protectedVars = mkOption { type = listOf str; default = [ "*pw*" "*pass*" "community" ]; - description = "List of string patterns for custom variables which should be excluded from user’s view."; + description = lib.mdDoc "List of string patterns for custom variables which should be excluded from user’s view."; }; }; mutableBackends = mkOption { type = bool; default = false; - description = "Make backends.ini of the monitoring module mutable (e.g. via the web interface)."; + description = lib.mdDoc "Make backends.ini of the monitoring module mutable (e.g. via the web interface)."; }; backends = mkOption { default = { icinga = { resource = "icinga_ido"; }; }; - description = "Monitoring backends to define"; + description = lib.mdDoc "Monitoring backends to define"; type = attrsOf (submodule ({ name, ... }: { options = { name = mkOption { @@ -71,13 +71,13 @@ in { resource = mkOption { type = str; - description = "Name of the IDO resource"; + description = lib.mdDoc "Name of the IDO resource"; }; disabled = mkOption { type = bool; default = false; - description = "Disable this backend"; + description = lib.mdDoc "Disable this backend"; }; }; })); @@ -86,12 +86,12 @@ in { mutableTransports = mkOption { type = bool; default = true; - description = "Make commandtransports.ini of the monitoring module mutable (e.g. via the web interface)."; + description = lib.mdDoc "Make commandtransports.ini of the monitoring module mutable (e.g. via the web interface)."; }; transports = mkOption { default = {}; - description = "Command transports to define"; + description = lib.mdDoc "Command transports to define"; type = attrsOf (submodule ({ name, ... }: { options = { name = mkOption { @@ -104,44 +104,44 @@ in { type = mkOption { type = enum [ "api" "local" "remote" ]; default = "api"; - description = "Type of this transport"; + description = lib.mdDoc "Type of this transport"; }; instance = mkOption { type = nullOr str; default = null; - description = "Assign a icinga instance to this transport"; + description = lib.mdDoc "Assign a icinga instance to this transport"; }; path = mkOption { type = str; - description = "Path to the socket for local or remote transports"; + description = lib.mdDoc "Path to the socket for local or remote transports"; }; host = mkOption { type = str; - description = "Host for the api or remote transport"; + description = lib.mdDoc "Host for the api or remote transport"; }; port = mkOption { type = nullOr str; default = null; - description = "Port to connect to for the api or remote transport"; + description = lib.mdDoc "Port to connect to for the api or remote transport"; }; username = mkOption { type = str; - description = "Username for the api or remote transport"; + description = lib.mdDoc "Username for the api or remote transport"; }; password = mkOption { type = str; - description = "Password for the api transport"; + description = lib.mdDoc "Password for the api transport"; }; resource = mkOption { type = str; - description = "SSH identity resource for the remote transport"; + description = lib.mdDoc "SSH identity resource for the remote transport"; }; }; })); diff --git a/nixpkgs/nixos/modules/services/web-apps/ihatemoney/default.nix b/nixpkgs/nixos/modules/services/web-apps/ihatemoney/default.nix index ad314c885ba8..c771f0afa231 100644 --- a/nixpkgs/nixos/modules/services/web-apps/ihatemoney/default.nix +++ b/nixpkgs/nixos/modules/services/web-apps/ihatemoney/default.nix @@ -51,42 +51,42 @@ in backend = mkOption { type = types.enum [ "sqlite" "postgresql" ]; default = "sqlite"; - description = '' + description = lib.mdDoc '' The database engine to use for ihatemoney. - If <literal>postgresql</literal> is selected, then a database called - <literal>${db}</literal> will be created. If you disable this option, + If `postgresql` is selected, then a database called + `${db}` will be created. If you disable this option, it will however not be removed. ''; }; adminHashedPassword = mkOption { type = types.nullOr types.str; default = null; - description = "The hashed password of the administrator. To obtain it, run <literal>ihatemoney generate_password_hash</literal>"; + description = lib.mdDoc "The hashed password of the administrator. To obtain it, run `ihatemoney generate_password_hash`"; }; uwsgiConfig = mkOption { type = types.attrs; example = { http = ":8000"; }; - description = "Additionnal configuration of the UWSGI vassal running ihatemoney. It should notably specify on which interfaces and ports the vassal should listen."; + description = lib.mdDoc "Additionnal configuration of the UWSGI vassal running ihatemoney. It should notably specify on which interfaces and ports the vassal should listen."; }; defaultSender = { name = mkOption { type = types.str; default = "Budget manager"; - description = "The display name of the sender of ihatemoney emails"; + description = lib.mdDoc "The display name of the sender of ihatemoney emails"; }; email = mkOption { type = types.str; default = "ihatemoney@${config.networking.hostName}"; defaultText = literalExpression ''"ihatemoney@''${config.networking.hostName}"''; - description = "The email of the sender of ihatemoney emails"; + description = lib.mdDoc "The email of the sender of ihatemoney emails"; }; }; secureCookie = mkOption { type = types.bool; default = true; - description = "Use secure cookies. Disable this when ihatemoney is served via http instead of https"; + description = lib.mdDoc "Use secure cookies. Disable this when ihatemoney is served via http instead of https"; }; enableDemoProject = mkEnableOption "access to the demo project in ihatemoney"; enablePublicProjectCreation = mkEnableOption "permission to create projects in ihatemoney by anyone"; @@ -95,12 +95,12 @@ in legalLink = mkOption { type = types.nullOr types.str; default = null; - description = "The URL to a page explaining legal statements about your service, eg. GDPR-related information."; + description = lib.mdDoc "The URL to a page explaining legal statements about your service, eg. GDPR-related information."; }; extraConfig = mkOption { type = types.str; default = ""; - description = "Extra configuration appended to ihatemoney's configuration file. It is a python file, so pay attention to indentation."; + description = lib.mdDoc "Extra configuration appended to ihatemoney's configuration file. It is a python file, so pay attention to indentation."; }; }; config = mkIf cfg.enable { diff --git a/nixpkgs/nixos/modules/services/web-apps/invidious.nix b/nixpkgs/nixos/modules/services/web-apps/invidious.nix index 10b30bf1fd1d..0b9d9b03c6ae 100644 --- a/nixpkgs/nixos/modules/services/web-apps/invidious.nix +++ b/nixpkgs/nixos/modules/services/web-apps/invidious.nix @@ -152,27 +152,27 @@ in type = types.package; default = pkgs.invidious; defaultText = "pkgs.invidious"; - description = "The Invidious package to use."; + description = lib.mdDoc "The Invidious package to use."; }; settings = lib.mkOption { type = settingsFormat.type; default = { }; - description = '' + description = lib.mdDoc '' The settings Invidious should use. - See <link xlink:href="https://github.com/iv-org/invidious/blob/master/config/config.example.yml">config.example.yml</link> for a list of all possible options. + See [config.example.yml](https://github.com/iv-org/invidious/blob/master/config/config.example.yml) for a list of all possible options. ''; }; extraSettingsFile = lib.mkOption { type = types.nullOr types.str; default = null; - description = '' + description = lib.mdDoc '' A file including Invidious settings. - It gets merged with the setttings specified in <option>services.invidious.settings</option> - and can be used to store secrets like <literal>hmac_key</literal> outside of the nix store. + It gets merged with the setttings specified in {option}`services.invidious.settings` + and can be used to store secrets like `hmac_key` outside of the nix store. ''; }; @@ -182,7 +182,7 @@ in domain = lib.mkOption { type = types.nullOr types.str; default = null; - description = '' + description = lib.mdDoc '' The FQDN Invidious is reachable on. This is used to configure nginx and for building absolute URLs. @@ -193,12 +193,12 @@ in type = types.port; # Default from https://docs.invidious.io/Configuration.md default = 3000; - description = '' + description = lib.mdDoc '' The port Invidious should listen on. To allow access from outside, - you can use either <option>services.invidious.nginx</option> - or add <literal>config.services.invidious.port</literal> to <option>networking.firewall.allowedTCPPorts</option>. + you can use either {option}`services.invidious.nginx` + or add `config.services.invidious.port` to {option}`networking.firewall.allowedTCPPorts`. ''; }; @@ -206,7 +206,7 @@ in createLocally = lib.mkOption { type = types.bool; default = true; - description = '' + description = lib.mdDoc '' Whether to create a local database with PostgreSQL. ''; }; @@ -214,10 +214,10 @@ in host = lib.mkOption { type = types.nullOr types.str; default = null; - description = '' + description = lib.mdDoc '' The database host Invidious should use. - If <literal>null</literal>, the local unix socket is used. Otherwise + If `null`, the local unix socket is used. Otherwise TCP is used. ''; }; @@ -226,7 +226,7 @@ in type = types.port; default = options.services.postgresql.port.default; defaultText = lib.literalExpression "options.services.postgresql.port.default"; - description = '' + description = lib.mdDoc '' The port of the database Invidious should use. Defaults to the the default postgresql port. @@ -237,7 +237,7 @@ in type = types.nullOr types.str; apply = lib.mapNullable toString; default = null; - description = '' + description = lib.mdDoc '' Path to file containing the database password. ''; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/invoiceplane.nix b/nixpkgs/nixos/modules/services/web-apps/invoiceplane.nix index 095eec36dec3..2a936027bd47 100644 --- a/nixpkgs/nixos/modules/services/web-apps/invoiceplane.nix +++ b/nixpkgs/nixos/modules/services/web-apps/invoiceplane.nix @@ -72,7 +72,7 @@ let stateDir = mkOption { type = types.path; default = "/var/lib/invoiceplane/${name}"; - description = '' + description = lib.mdDoc '' This directory is used for uploads of attachements and cache. The directory passed here is automatically created and permissions adjusted as required. @@ -83,41 +83,41 @@ let host = mkOption { type = types.str; default = "localhost"; - description = "Database host address."; + description = lib.mdDoc "Database host address."; }; port = mkOption { type = types.port; default = 3306; - description = "Database host port."; + description = lib.mdDoc "Database host port."; }; name = mkOption { type = types.str; default = "invoiceplane"; - description = "Database name."; + description = lib.mdDoc "Database name."; }; user = mkOption { type = types.str; default = "invoiceplane"; - description = "Database user."; + description = lib.mdDoc "Database user."; }; passwordFile = mkOption { type = types.nullOr types.path; default = null; example = "/run/keys/invoiceplane-dbpassword"; - description = '' + description = lib.mdDoc '' A file containing the password corresponding to - <option>database.user</option>. + {option}`database.user`. ''; }; createLocally = mkOption { type = types.bool; default = true; - description = "Create the database and database user locally."; + description = lib.mdDoc "Create the database and database user locally."; }; }; @@ -160,8 +160,8 @@ let "pm.max_spare_servers" = 4; "pm.max_requests" = 500; }; - description = '' - Options for the InvoicePlane PHP pool. See the documentation on <literal>php-fpm.conf</literal> + description = lib.mdDoc '' + Options for the InvoicePlane PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives. ''; }; @@ -174,9 +174,9 @@ let DISABLE_SETUP=true IP_URL=https://invoice.example.com ''; - description = '' + description = lib.mdDoc '' InvoicePlane configuration. Refer to - <link xlink:href="https://github.com/InvoicePlane/InvoicePlane/blob/master/ipconfig.php.example"/> + <https://github.com/InvoicePlane/InvoicePlane/blob/master/ipconfig.php.example> for details on supported values. ''; }; @@ -194,20 +194,20 @@ in options.sites = mkOption { type = types.attrsOf (types.submodule siteOpts); default = {}; - description = "Specification of one or more WordPress sites to serve"; + description = lib.mdDoc "Specification of one or more WordPress sites to serve"; }; options.webserver = mkOption { type = types.enum [ "caddy" ]; default = "caddy"; - description = '' + description = lib.mdDoc '' Which webserver to use for virtual host management. Currently only caddy is supported. ''; }; }; default = {}; - description = "InvoicePlane configuration."; + description = lib.mdDoc "InvoicePlane configuration."; }; }; @@ -236,7 +236,7 @@ in }; services.phpfpm = { - phpPackage = pkgs.php74; + phpPackage = pkgs.php81; pools = mapAttrs' (hostName: cfg: ( nameValuePair "invoiceplane-${hostName}" { inherit user; @@ -302,4 +302,3 @@ in ]); } - diff --git a/nixpkgs/nixos/modules/services/web-apps/jirafeau.nix b/nixpkgs/nixos/modules/services/web-apps/jirafeau.nix index 328c61c8e646..c95d8ffd524d 100644 --- a/nixpkgs/nixos/modules/services/web-apps/jirafeau.nix +++ b/nixpkgs/nixos/modules/services/web-apps/jirafeau.nix @@ -25,7 +25,7 @@ in adminPasswordSha256 = mkOption { type = types.str; default = ""; - description = '' + description = lib.mdDoc '' SHA-256 of the desired administration password. Leave blank/unset for no password. ''; }; @@ -33,7 +33,7 @@ in dataDir = mkOption { type = types.path; default = "/var/lib/jirafeau/data/"; - description = "Location of Jirafeau storage directory."; + description = lib.mdDoc "Location of Jirafeau storage directory."; }; enable = mkEnableOption "Jirafeau file upload application."; @@ -58,13 +58,13 @@ in hostName = mkOption { type = types.str; default = "localhost"; - description = "URL of instance. Must have trailing slash."; + description = lib.mdDoc "URL of instance. Must have trailing slash."; }; maxUploadSizeMegabytes = mkOption { type = types.int; default = 0; - description = "Maximum upload size of accepted files."; + description = lib.mdDoc "Maximum upload size of accepted files."; }; maxUploadTimeout = mkOption { @@ -89,14 +89,14 @@ in serverAliases = [ "wiki.''${config.networking.domain}" ]; } ''; - description = "Extra configuration for the nginx virtual host of Jirafeau."; + description = lib.mdDoc "Extra configuration for the nginx virtual host of Jirafeau."; }; package = mkOption { type = types.package; default = pkgs.jirafeau; defaultText = literalExpression "pkgs.jirafeau"; - description = "Jirafeau package to use"; + description = lib.mdDoc "Jirafeau package to use"; }; poolConfig = mkOption { @@ -109,8 +109,8 @@ in "pm.max_spare_servers" = 4; "pm.max_requests" = 500; }; - description = '' - Options for Jirafeau PHP pool. See documentation on <literal>php-fpm.conf</literal> for + description = lib.mdDoc '' + Options for Jirafeau PHP pool. See documentation on `php-fpm.conf` for details on configuration directives. ''; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/jitsi-meet.nix b/nixpkgs/nixos/modules/services/web-apps/jitsi-meet.nix index 2f1c4acec1e8..b38a510bb87e 100644 --- a/nixpkgs/nixos/modules/services/web-apps/jitsi-meet.nix +++ b/nixpkgs/nixos/modules/services/web-apps/jitsi-meet.nix @@ -51,7 +51,7 @@ in hostName = mkOption { type = str; example = "meet.example.org"; - description = '' + description = lib.mdDoc '' FQDN of the Jitsi Meet instance. ''; }; @@ -65,10 +65,10 @@ in defaultLang = "fi"; } ''; - description = '' - Client-side web application settings that override the defaults in <filename>config.js</filename>. + description = lib.mdDoc '' + Client-side web application settings that override the defaults in {file}`config.js`. - See <link xlink:href="https://github.com/jitsi/jitsi-meet/blob/master/config.js" /> for default + See <https://github.com/jitsi/jitsi-meet/blob/master/config.js> for default configuration with comments. ''; }; @@ -76,8 +76,8 @@ in extraConfig = mkOption { type = lines; default = ""; - description = '' - Text to append to <filename>config.js</filename> web application config file. + description = lib.mdDoc '' + Text to append to {file}`config.js` web application config file. Can be used to insert JavaScript logic to determine user's region in cascading bridges setup. ''; @@ -92,10 +92,10 @@ in SHOW_WATERMARK_FOR_GUESTS = false; } ''; - description = '' - Client-side web-app interface settings that override the defaults in <filename>interface_config.js</filename>. + description = lib.mdDoc '' + Client-side web-app interface settings that override the defaults in {file}`interface_config.js`. - See <link xlink:href="https://github.com/jitsi/jitsi-meet/blob/master/interface_config.js" /> for + See <https://github.com/jitsi/jitsi-meet/blob/master/interface_config.js> for default configuration with comments. ''; }; @@ -104,10 +104,10 @@ in enable = mkOption { type = bool; default = true; - description = '' + description = lib.mdDoc '' Whether to enable Jitsi Videobridge instance and configure it to connect to Prosody. - Additional configuration is possible with <option>services.jitsi-videobridge</option>. + Additional configuration is possible with {option}`services.jitsi-videobridge`. ''; }; @@ -115,10 +115,10 @@ in type = nullOr str; default = null; example = "/run/keys/videobridge"; - description = '' + description = lib.mdDoc '' File containing password to the Prosody account for videobridge. - If <literal>null</literal>, a file with password will be generated automatically. Setting + If `null`, a file with password will be generated automatically. Setting this option is useful if you plan to connect additional videobridges to the XMPP server. ''; }; @@ -127,44 +127,44 @@ in jicofo.enable = mkOption { type = bool; default = true; - description = '' + description = lib.mdDoc '' Whether to enable JiCoFo instance and configure it to connect to Prosody. - Additional configuration is possible with <option>services.jicofo</option>. + Additional configuration is possible with {option}`services.jicofo`. ''; }; jibri.enable = mkOption { type = bool; default = false; - description = '' + description = lib.mdDoc '' Whether to enable a Jibri instance and configure it to connect to Prosody. - Additional configuration is possible with <option>services.jibri</option>, and - <option>services.jibri.finalizeScript</option> is especially useful. + Additional configuration is possible with {option}`services.jibri`, and + {option}`services.jibri.finalizeScript` is especially useful. ''; }; nginx.enable = mkOption { type = bool; default = true; - description = '' + description = lib.mdDoc '' Whether to enable nginx virtual host that will serve the javascript application and act as a proxy for the XMPP server. Further nginx configuration can be done by adapting - <option>services.nginx.virtualHosts.<hostName></option>. + {option}`services.nginx.virtualHosts.<hostName>`. When this is enabled, ACME will be used to retrieve a TLS certificate by default. To disable - this, set the <option>services.nginx.virtualHosts.<hostName>.enableACME</option> to - <literal>false</literal> and if appropriate do the same for - <option>services.nginx.virtualHosts.<hostName>.forceSSL</option>. + this, set the {option}`services.nginx.virtualHosts.<hostName>.enableACME` to + `false` and if appropriate do the same for + {option}`services.nginx.virtualHosts.<hostName>.forceSSL`. ''; }; - caddy.enable = mkEnableOption "Whether to enablle caddy reverse proxy to expose jitsi-meet"; + caddy.enable = mkEnableOption "Whether to enable caddy reverse proxy to expose jitsi-meet"; prosody.enable = mkOption { type = bool; default = true; - description = '' + description = lib.mdDoc '' Whether to configure Prosody to relay XMPP messages between Jitsi Meet components. Turn this off if you want to configure it manually. ''; @@ -253,9 +253,20 @@ in ''; }; }; - systemd.services.prosody.serviceConfig = mkIf cfg.prosody.enable { - EnvironmentFile = [ "/var/lib/jitsi-meet/secrets-env" ]; - SupplementaryGroups = [ "jitsi-meet" ]; + systemd.services.prosody = mkIf cfg.prosody.enable { + preStart = let + videobridgeSecret = if cfg.videobridge.passwordFile != null then cfg.videobridge.passwordFile else "/var/lib/jitsi-meet/videobridge-secret"; + in '' + ${config.services.prosody.package}/bin/prosodyctl register focus auth.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jicofo-user-secret)" + ${config.services.prosody.package}/bin/prosodyctl register jvb auth.${cfg.hostName} "$(cat ${videobridgeSecret})" + ${config.services.prosody.package}/bin/prosodyctl mod_roster_command subscribe focus.${cfg.hostName} focus@auth.${cfg.hostName} + ${config.services.prosody.package}/bin/prosodyctl register jibri auth.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jibri-auth-secret)" + ${config.services.prosody.package}/bin/prosodyctl register recorder recorder.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jibri-recorder-secret)" + ''; + serviceConfig = { + EnvironmentFile = [ "/var/lib/jitsi-meet/secrets-env" ]; + SupplementaryGroups = [ "jitsi-meet" ]; + }; }; users.groups.jitsi-meet = {}; @@ -266,14 +277,12 @@ in systemd.services.jitsi-meet-init-secrets = { wantedBy = [ "multi-user.target" ]; before = [ "jicofo.service" "jitsi-videobridge2.service" ] ++ (optional cfg.prosody.enable "prosody.service"); - path = [ config.services.prosody.package ]; serviceConfig = { Type = "oneshot"; }; script = let secrets = [ "jicofo-component-secret" "jicofo-user-secret" "jibri-auth-secret" "jibri-recorder-secret" ] ++ (optional (cfg.videobridge.passwordFile == null) "videobridge-secret"); - videobridgeSecret = if cfg.videobridge.passwordFile != null then cfg.videobridge.passwordFile else "/var/lib/jitsi-meet/videobridge-secret"; in '' cd /var/lib/jitsi-meet @@ -291,12 +300,6 @@ in chmod 640 secrets-env '' + optionalString cfg.prosody.enable '' - prosodyctl register focus auth.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jicofo-user-secret)" - prosodyctl register jvb auth.${cfg.hostName} "$(cat ${videobridgeSecret})" - prosodyctl mod_roster_command subscribe focus.${cfg.hostName} focus@auth.${cfg.hostName} - prosodyctl register jibri auth.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jibri-auth-secret)" - prosodyctl register recorder recorder.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jibri-recorder-secret)" - # generate self-signed certificates if [ ! -f /var/lib/jitsi-meet.crt ]; then ${getBin pkgs.openssl}/bin/openssl req \ diff --git a/nixpkgs/nixos/modules/services/web-apps/keycloak.nix b/nixpkgs/nixos/modules/services/web-apps/keycloak.nix index c4a2127663a9..b878cb74b52e 100644 --- a/nixpkgs/nixos/modules/services/web-apps/keycloak.nix +++ b/nixpkgs/nixos/modules/services/web-apps/keycloak.nix @@ -4,115 +4,114 @@ let cfg = config.services.keycloak; opt = options.services.keycloak; - inherit (lib) types mkOption concatStringsSep mapAttrsToList - escapeShellArg recursiveUpdate optionalAttrs boolToString mkOrder - sort filterAttrs concatMapStringsSep concatStrings mkIf - optionalString optionals mkDefault literalExpression hasSuffix - foldl' isAttrs filter attrNames elem literalDocBook - maintainers; - - inherit (builtins) match typeOf; + inherit (lib) + types + mkMerge + mkOption + mkChangedOptionModule + mkRenamedOptionModule + mkRemovedOptionModule + concatStringsSep + mapAttrsToList + escapeShellArg + mkIf + optionalString + optionals + mkDefault + literalExpression + isAttrs + literalDocBook + maintainers + catAttrs + collect + splitString + ; + + inherit (builtins) + elem + typeOf + isInt + isString + hashString + isPath + ; + + prefixUnlessEmpty = prefix: string: optionalString (string != "") "${prefix}${string}"; in { + imports = + [ + (mkRenamedOptionModule + [ "services" "keycloak" "bindAddress" ] + [ "services" "keycloak" "settings" "http-host" ]) + (mkRenamedOptionModule + [ "services" "keycloak" "forceBackendUrlToFrontendUrl"] + [ "services" "keycloak" "settings" "hostname-strict-backchannel"]) + (mkChangedOptionModule + [ "services" "keycloak" "httpPort" ] + [ "services" "keycloak" "settings" "http-port" ] + (config: + builtins.fromJSON config.services.keycloak.httpPort)) + (mkChangedOptionModule + [ "services" "keycloak" "httpsPort" ] + [ "services" "keycloak" "settings" "https-port" ] + (config: + builtins.fromJSON config.services.keycloak.httpsPort)) + (mkRemovedOptionModule + [ "services" "keycloak" "frontendUrl" ] + '' + Set `services.keycloak.settings.hostname' and `services.keycloak.settings.http-relative-path' instead. + NOTE: You likely want to set 'http-relative-path' to '/auth' to keep compatibility with your clients. + See its description for more information. + '') + (mkRemovedOptionModule + [ "services" "keycloak" "extraConfig" ] + "Use `services.keycloak.settings' instead.") + ]; + options.services.keycloak = let - inherit (types) bool str nullOr attrsOf path enum anything - package port; + inherit (types) + bool + str + int + nullOr + attrsOf + oneOf + path + enum + package + port; + + assertStringPath = optionName: value: + if isPath value then + throw '' + services.keycloak.${optionName}: + ${toString value} + is a Nix path, but should be a string, since Nix + paths are copied into the world-readable Nix store. + '' + else value; in { enable = mkOption { type = bool; default = false; example = true; - description = '' + description = lib.mdDoc '' Whether to enable the Keycloak identity and access management server. ''; }; - bindAddress = mkOption { - type = str; - default = "\${jboss.bind.address:0.0.0.0}"; - example = "127.0.0.1"; - description = '' - On which address Keycloak should accept new connections. - - A special syntax can be used to allow command line Java system - properties to override the value: ''${property.name:value} - ''; - }; - - httpPort = mkOption { - type = str; - default = "\${jboss.http.port:80}"; - example = "8080"; - description = '' - On which port Keycloak should listen for new HTTP connections. - - A special syntax can be used to allow command line Java system - properties to override the value: ''${property.name:value} - ''; - }; - - httpsPort = mkOption { - type = str; - default = "\${jboss.https.port:443}"; - example = "8443"; - description = '' - On which port Keycloak should listen for new HTTPS connections. - - A special syntax can be used to allow command line Java system - properties to override the value: ''${property.name:value} - ''; - }; - - frontendUrl = mkOption { - type = str; - apply = x: - if x == "" || hasSuffix "/" x then - x - else - x + "/"; - example = "keycloak.example.com/auth"; - description = '' - The public URL used as base for all frontend requests. Should - normally include a trailing <literal>/auth</literal>. - - See <link xlink:href="https://www.keycloak.org/docs/latest/server_installation/#_hostname">the - Hostname section of the Keycloak server installation - manual</link> for more information. - ''; - }; - - forceBackendUrlToFrontendUrl = mkOption { - type = bool; - default = false; - example = true; - description = '' - Whether Keycloak should force all requests to go through the - frontend URL configured in <xref - linkend="opt-services.keycloak.frontendUrl" />. By default, - Keycloak allows backend requests to instead use its local - hostname or IP address and may also advertise it to clients - through its OpenID Connect Discovery endpoint. - - See <link - xlink:href="https://www.keycloak.org/docs/latest/server_installation/#_hostname">the - Hostname section of the Keycloak server installation - manual</link> for more information. - ''; - }; - sslCertificate = mkOption { type = nullOr path; default = null; example = "/run/keys/ssl_cert"; - description = '' + apply = assertStringPath "sslCertificate"; + description = lib.mdDoc '' The path to a PEM formatted certificate to use for TLS/SSL connections. - - This should be a string, not a Nix path, since Nix paths are - copied into the world-readable Nix store. ''; }; @@ -120,29 +119,29 @@ in type = nullOr path; default = null; example = "/run/keys/ssl_key"; - description = '' + apply = assertStringPath "sslCertificateKey"; + description = lib.mdDoc '' The path to a PEM formatted private key to use for TLS/SSL connections. - - This should be a string, not a Nix path, since Nix paths are - copied into the world-readable Nix store. ''; }; plugins = lib.mkOption { type = lib.types.listOf lib.types.path; - default = []; - description = '' - Keycloak plugin jar, ear files or derivations with them + default = [ ]; + description = lib.mdDoc '' + Keycloak plugin jar, ear files or derivations containing + them. Packaged plugins are available through + `pkgs.keycloak.plugins`. ''; }; database = { type = mkOption { - type = enum [ "mysql" "postgresql" ]; + type = enum [ "mysql" "mariadb" "postgresql" ]; default = "postgresql"; - example = "mysql"; - description = '' + example = "mariadb"; + description = lib.mdDoc '' The type of database Keycloak should connect to. ''; }; @@ -150,7 +149,7 @@ in host = mkOption { type = str; default = "localhost"; - description = '' + description = lib.mdDoc '' Hostname of the database to connect to. ''; }; @@ -159,6 +158,7 @@ in let dbPorts = { postgresql = 5432; + mariadb = 3306; mysql = 3306; }; in @@ -166,7 +166,7 @@ in type = port; default = dbPorts.${cfg.database.type}; defaultText = literalDocBook "default port of selected database"; - description = '' + description = lib.mdDoc '' Port of the database to connect to. ''; }; @@ -175,7 +175,7 @@ in type = bool; default = cfg.database.host != "localhost"; defaultText = literalExpression ''config.${opt.database.host} != "localhost"''; - description = '' + description = lib.mdDoc '' Whether the database connection should be secured by SSL / TLS. ''; @@ -184,13 +184,13 @@ in caCert = mkOption { type = nullOr path; default = null; - description = '' + description = lib.mdDoc '' The SSL / TLS CA certificate that verifies the identity of the database server. Required when PostgreSQL is used and SSL is turned on. - For MySQL, if left at <literal>null</literal>, the default + For MySQL, if left at `null`, the default Java keystore is used, which should suffice if the server certificate is issued by an official CA. ''; @@ -199,7 +199,7 @@ in createLocally = mkOption { type = bool; default = true; - description = '' + description = lib.mdDoc '' Whether a database should be automatically created on the local host. Set this to false if you plan on provisioning a local database yourself. This has no effect if @@ -207,30 +207,40 @@ in ''; }; + name = mkOption { + type = str; + default = "keycloak"; + description = lib.mdDoc '' + Database name to use when connecting to an external or + manually provisioned database; has no effect when a local + database is automatically provisioned. + + To use this with a local database, set [](#opt-services.keycloak.database.createLocally) to + `false` and create the database and user + manually. + ''; + }; + username = mkOption { type = str; default = "keycloak"; - description = '' + description = lib.mdDoc '' Username to use when connecting to an external or manually provisioned database; has no effect when a local database is automatically provisioned. - To use this with a local database, set <xref - linkend="opt-services.keycloak.database.createLocally" /> to - <literal>false</literal> and create the database and user - manually. The database should be called - <literal>keycloak</literal>. + To use this with a local database, set [](#opt-services.keycloak.database.createLocally) to + `false` and create the database and user + manually. ''; }; passwordFile = mkOption { type = path; example = "/run/keys/db_password"; - description = '' - File containing the database password. - - This should be a string, not a Nix path, since Nix paths are - copied into the world-readable Nix store. + apply = assertStringPath "passwordFile"; + description = lib.mdDoc '' + The path to a file containing the database password. ''; }; }; @@ -239,7 +249,7 @@ in type = package; default = pkgs.keycloak; defaultText = literalExpression "pkgs.keycloak"; - description = '' + description = lib.mdDoc '' Keycloak package to use. ''; }; @@ -247,8 +257,8 @@ in initialAdminPassword = mkOption { type = str; default = "changeme"; - description = '' - Initial password set for the <literal>admin</literal> + description = lib.mdDoc '' + Initial password set for the `admin` user. The password is not stored safely and should be changed immediately in the admin panel. ''; @@ -257,78 +267,187 @@ in themes = mkOption { type = attrsOf package; default = { }; - description = '' + description = lib.mdDoc '' Additional theme packages for Keycloak. Each theme is linked into subdirectory with a corresponding attribute name. Theme packages consist of several subdirectories which provide - different theme types: for example, <literal>account</literal>, - <literal>login</literal> etc. After adding a theme to this option you + different theme types: for example, `account`, + `login` etc. After adding a theme to this option you can select it by its name in Keycloak administration console. ''; }; - extraConfig = mkOption { - type = attrsOf anything; - default = { }; + settings = mkOption { + type = lib.types.submodule { + freeformType = attrsOf (nullOr (oneOf [ str int bool (attrsOf path) ])); + + options = { + http-host = mkOption { + type = str; + default = "0.0.0.0"; + example = "127.0.0.1"; + description = lib.mdDoc '' + On which address Keycloak should accept new connections. + ''; + }; + + http-port = mkOption { + type = port; + default = 80; + example = 8080; + description = lib.mdDoc '' + On which port Keycloak should listen for new HTTP connections. + ''; + }; + + https-port = mkOption { + type = port; + default = 443; + example = 8443; + description = lib.mdDoc '' + On which port Keycloak should listen for new HTTPS connections. + ''; + }; + + http-relative-path = mkOption { + type = str; + default = ""; + example = "/auth"; + description = '' + The path relative to <literal>/</literal> for serving + resources. + + <note> + <para> + In versions of Keycloak using Wildfly (<17), + this defaulted to <literal>/auth</literal>. If + upgrading from the Wildfly version of Keycloak, + i.e. a NixOS version before 22.05, you'll likely + want to set this to <literal>/auth</literal> to + keep compatibility with your clients. + + See <link xlink:href="https://www.keycloak.org/migration/migrating-to-quarkus"/> + for more information on migrating from Wildfly to Quarkus. + </para> + </note> + ''; + }; + + hostname = mkOption { + type = str; + example = "keycloak.example.com"; + description = lib.mdDoc '' + The hostname part of the public URL used as base for + all frontend requests. + + See <https://www.keycloak.org/server/hostname> + for more information about hostname configuration. + ''; + }; + + hostname-strict-backchannel = mkOption { + type = bool; + default = false; + example = true; + description = lib.mdDoc '' + Whether Keycloak should force all requests to go + through the frontend URL. By default, Keycloak allows + backend requests to instead use its local hostname or + IP address and may also advertise it to clients + through its OpenID Connect Discovery endpoint. + + See <https://www.keycloak.org/server/hostname> + for more information about hostname configuration. + ''; + }; + + proxy = mkOption { + type = enum [ "edge" "reencrypt" "passthrough" "none" ]; + default = "none"; + example = "edge"; + description = '' + The proxy address forwarding mode if the server is + behind a reverse proxy. + + <variablelist> + <varlistentry> + <term>edge</term> + <listitem> + <para> + Enables communication through HTTP between the + proxy and Keycloak. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>reencrypt</term> + <listitem> + <para> + Requires communication through HTTPS between the + proxy and Keycloak. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>passthrough</term> + <listitem> + <para> + Enables communication through HTTP or HTTPS between + the proxy and Keycloak. + </para> + </listitem> + </varlistentry> + </variablelist> + + See <link xlink:href="https://www.keycloak.org/server/reverseproxy"/> for more information. + ''; + }; + }; + }; + example = literalExpression '' { - "subsystem=keycloak-server" = { - "spi=hostname" = { - "provider=default" = null; - "provider=fixed" = { - enabled = true; - properties.hostname = "keycloak.example.com"; - }; - default-provider = "fixed"; - }; - }; + hostname = "keycloak.example.com"; + proxy = "reencrypt"; + https-key-store-file = "/path/to/file"; + https-key-store-password = { _secret = "/run/keys/store_password"; }; } ''; - description = '' - Additional Keycloak configuration options to set in - <literal>standalone.xml</literal>. - - Options are expressed as a Nix attribute set which matches the - structure of the jboss-cli configuration. The configuration is - effectively overlayed on top of the default configuration - shipped with Keycloak. To remove existing nodes and undefine - attributes from the default configuration, set them to - <literal>null</literal>. - - The example configuration does the equivalent of the following - script, which removes the hostname provider - <literal>default</literal>, adds the deprecated hostname - provider <literal>fixed</literal> and defines it the default: - - <programlisting> - /subsystem=keycloak-server/spi=hostname/provider=default:remove() - /subsystem=keycloak-server/spi=hostname/provider=fixed:add(enabled = true, properties = { hostname = "keycloak.example.com" }) - /subsystem=keycloak-server/spi=hostname:write-attribute(name=default-provider, value="fixed") - </programlisting> - - You can discover available options by using the <link - xlink:href="http://docs.wildfly.org/21/Admin_Guide.html#Command_Line_Interface">jboss-cli.sh</link> - program and by referring to the <link - xlink:href="https://www.keycloak.org/docs/latest/server_installation/index.html">Keycloak - Server Installation and Configuration Guide</link>. + + description = lib.mdDoc '' + Configuration options corresponding to parameters set in + {file}`conf/keycloak.conf`. + + Most available options are documented at <https://www.keycloak.org/server/all-config>. + + Options containing secret data should be set to an attribute + set containing the attribute `_secret` - a + string pointing to a file containing the value the option + should be set to. See the example to get a better picture of + this: in the resulting + {file}`conf/keycloak.conf` file, the + `https-key-store-password` key will be set + to the contents of the + {file}`/run/keys/store_password` file. ''; }; - }; config = let - # We only want to create a database if we're actually going to connect to it. + # We only want to create a database if we're actually going to + # connect to it. databaseActuallyCreateLocally = cfg.database.createLocally && cfg.database.host == "localhost"; createLocalPostgreSQL = databaseActuallyCreateLocally && cfg.database.type == "postgresql"; - createLocalMySQL = databaseActuallyCreateLocally && cfg.database.type == "mysql"; + createLocalMySQL = databaseActuallyCreateLocally && elem cfg.database.type [ "mysql" "mariadb" ]; mySqlCaKeystore = pkgs.runCommand "mysql-ca-keystore" { } '' ${pkgs.jre}/bin/keytool -importcert -trustcacerts -alias MySQLCACert -file ${cfg.database.caCert} -keystore $out -storepass notsosecretpassword -noprompt ''; - # Both theme and theme type directories need to be actual directories in one hierarchy to pass Keycloak checks. + # Both theme and theme type directories need to be actual + # directories in one hierarchy to pass Keycloak checks. themesBundle = pkgs.runCommand "keycloak-themes" { } '' linkTheme() { theme="$1" @@ -347,7 +466,7 @@ in } mkdir -p "$out" - for theme in ${cfg.package}/themes/*; do + for theme in ${keycloakBuild}/themes/*; do if [ -d "$theme" ]; then linkTheme "$theme" "$(basename "$theme")" fi @@ -356,329 +475,25 @@ in ${concatStringsSep "\n" (mapAttrsToList (name: theme: "linkTheme ${theme} ${escapeShellArg name}") cfg.themes)} ''; - keycloakConfig' = foldl' recursiveUpdate - { - "interface=public".inet-address = cfg.bindAddress; - "socket-binding-group=standard-sockets"."socket-binding=http".port = cfg.httpPort; - "subsystem=keycloak-server" = { - "spi=hostname"."provider=default" = { - enabled = true; - properties = { - inherit (cfg) frontendUrl forceBackendUrlToFrontendUrl; - }; - }; - "theme=defaults".dir = toString themesBundle; - }; - "subsystem=datasources"."data-source=KeycloakDS" = { - max-pool-size = "20"; - user-name = if databaseActuallyCreateLocally then "keycloak" else cfg.database.username; - password = "@db-password@"; - }; - } [ - (optionalAttrs (cfg.database.type == "postgresql") { - "subsystem=datasources" = { - "jdbc-driver=postgresql" = { - driver-module-name = "org.postgresql"; - driver-name = "postgresql"; - driver-xa-datasource-class-name = "org.postgresql.xa.PGXADataSource"; - }; - "data-source=KeycloakDS" = { - connection-url = "jdbc:postgresql://${cfg.database.host}:${toString cfg.database.port}/keycloak"; - driver-name = "postgresql"; - "connection-properties=ssl".value = boolToString cfg.database.useSSL; - } // (optionalAttrs (cfg.database.caCert != null) { - "connection-properties=sslrootcert".value = cfg.database.caCert; - "connection-properties=sslmode".value = "verify-ca"; - }); - }; - }) - (optionalAttrs (cfg.database.type == "mysql") { - "subsystem=datasources" = { - "jdbc-driver=mysql" = { - driver-module-name = "com.mysql"; - driver-name = "mysql"; - driver-class-name = "com.mysql.jdbc.Driver"; - }; - "data-source=KeycloakDS" = { - connection-url = "jdbc:mysql://${cfg.database.host}:${toString cfg.database.port}/keycloak"; - driver-name = "mysql"; - "connection-properties=useSSL".value = boolToString cfg.database.useSSL; - "connection-properties=requireSSL".value = boolToString cfg.database.useSSL; - "connection-properties=verifyServerCertificate".value = boolToString cfg.database.useSSL; - "connection-properties=characterEncoding".value = "UTF-8"; - valid-connection-checker-class-name = "org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker"; - validate-on-match = true; - exception-sorter-class-name = "org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter"; - } // (optionalAttrs (cfg.database.caCert != null) { - "connection-properties=trustCertificateKeyStoreUrl".value = "file:${mySqlCaKeystore}"; - "connection-properties=trustCertificateKeyStorePassword".value = "notsosecretpassword"; - }); - }; - }) - (optionalAttrs (cfg.sslCertificate != null && cfg.sslCertificateKey != null) { - "socket-binding-group=standard-sockets"."socket-binding=https".port = cfg.httpsPort; - "subsystem=elytron" = mkOrder 900 { - "key-store=httpsKS" = mkOrder 900 { - path = "/run/keycloak/ssl/certificate_private_key_bundle.p12"; - credential-reference.clear-text = "notsosecretpassword"; - type = "JKS"; - }; - "key-manager=httpsKM" = mkOrder 901 { - key-store = "httpsKS"; - credential-reference.clear-text = "notsosecretpassword"; - }; - "server-ssl-context=httpsSSC" = mkOrder 902 { - key-manager = "httpsKM"; - }; - }; - "subsystem=undertow" = mkOrder 901 { - "server=default-server"."https-listener=https".ssl-context = "httpsSSC"; - }; - }) - cfg.extraConfig - ]; - - - /* Produces a JBoss CLI script that creates paths and sets - attributes matching those described by `attrs`. When the - script is run, the existing settings are effectively overlayed - by those from `attrs`. Existing attributes can be unset by - defining them `null`. - - JBoss paths and attributes / maps are distinguished by their - name, where paths follow a `key=value` scheme. - - Example: - mkJbossScript { - "subsystem=keycloak-server"."spi=hostname" = { - "provider=fixed" = null; - "provider=default" = { - enabled = true; - properties = { - inherit frontendUrl; - forceBackendUrlToFrontendUrl = false; - }; - }; - }; - } - => '' - if (outcome != success) of /:read-resource() - /:add() - end-if - if (outcome != success) of /subsystem=keycloak-server:read-resource() - /subsystem=keycloak-server:add() - end-if - if (outcome != success) of /subsystem=keycloak-server/spi=hostname:read-resource() - /subsystem=keycloak-server/spi=hostname:add() - end-if - if (outcome != success) of /subsystem=keycloak-server/spi=hostname/provider=default:read-resource() - /subsystem=keycloak-server/spi=hostname/provider=default:add(enabled = true, properties = { forceBackendUrlToFrontendUrl = false, frontendUrl = "https://keycloak.example.com/auth" }) - end-if - if (result != true) of /subsystem=keycloak-server/spi=hostname/provider=default:read-attribute(name="enabled") - /subsystem=keycloak-server/spi=hostname/provider=default:write-attribute(name=enabled, value=true) - end-if - if (result != false) of /subsystem=keycloak-server/spi=hostname/provider=default:read-attribute(name="properties.forceBackendUrlToFrontendUrl") - /subsystem=keycloak-server/spi=hostname/provider=default:write-attribute(name=properties.forceBackendUrlToFrontendUrl, value=false) - end-if - if (result != "https://keycloak.example.com/auth") of /subsystem=keycloak-server/spi=hostname/provider=default:read-attribute(name="properties.frontendUrl") - /subsystem=keycloak-server/spi=hostname/provider=default:write-attribute(name=properties.frontendUrl, value="https://keycloak.example.com/auth") - end-if - if (outcome != success) of /subsystem=keycloak-server/spi=hostname/provider=fixed:read-resource() - /subsystem=keycloak-server/spi=hostname/provider=fixed:remove() - end-if - '' - */ - mkJbossScript = attrs: - let - /* From a JBoss path and an attrset, produces a JBoss CLI - snippet that writes the corresponding attributes starting - at `path`. Recurses down into subattrsets as necessary, - producing the variable name from its full path in the - attrset. - - Example: - writeAttributes "/subsystem=keycloak-server/spi=hostname/provider=default" { - enabled = true; - properties = { - forceBackendUrlToFrontendUrl = false; - frontendUrl = "https://keycloak.example.com/auth"; - }; - } - => '' - if (result != true) of /subsystem=keycloak-server/spi=hostname/provider=default:read-attribute(name="enabled") - /subsystem=keycloak-server/spi=hostname/provider=default:write-attribute(name=enabled, value=true) - end-if - if (result != false) of /subsystem=keycloak-server/spi=hostname/provider=default:read-attribute(name="properties.forceBackendUrlToFrontendUrl") - /subsystem=keycloak-server/spi=hostname/provider=default:write-attribute(name=properties.forceBackendUrlToFrontendUrl, value=false) - end-if - if (result != "https://keycloak.example.com/auth") of /subsystem=keycloak-server/spi=hostname/provider=default:read-attribute(name="properties.frontendUrl") - /subsystem=keycloak-server/spi=hostname/provider=default:write-attribute(name=properties.frontendUrl, value="https://keycloak.example.com/auth") - end-if - '' - */ - writeAttributes = path: set: - let - # JBoss expressions like `${var}` need to be prefixed - # with `expression` to evaluate. - prefixExpression = string: - let - matchResult = match ''"\$\{.*}"'' string; - in - if matchResult != null then - "expression " + string - else - string; - - writeAttribute = attribute: value: - let - type = typeOf value; - in - if type == "set" then - let - names = attrNames value; - in - foldl' (text: name: text + (writeAttribute "${attribute}.${name}" value.${name})) "" names - else if value == null then '' - if (outcome == success) of ${path}:read-attribute(name="${attribute}") - ${path}:undefine-attribute(name="${attribute}") - end-if - '' - else if elem type [ "string" "path" "bool" ] then - let - value' = if type == "bool" then boolToString value else ''"${value}"''; - in - '' - if (result != ${prefixExpression value'}) of ${path}:read-attribute(name="${attribute}") - ${path}:write-attribute(name=${attribute}, value=${value'}) - end-if - '' - else throw "Unsupported type '${type}' for path '${path}'!"; - in - concatStrings - (mapAttrsToList - (attribute: value: (writeAttribute attribute value)) - set); - - - /* Produces an argument list for the JBoss `add()` function, - which adds a JBoss path and takes as its arguments the - required subpaths and attributes. - - Example: - makeArgList { - enabled = true; - properties = { - forceBackendUrlToFrontendUrl = false; - frontendUrl = "https://keycloak.example.com/auth"; - }; - } - => '' - enabled = true, properties = { forceBackendUrlToFrontendUrl = false, frontendUrl = "https://keycloak.example.com/auth" } - '' - */ - makeArgList = set: - let - makeArg = attribute: value: - let - type = typeOf value; - in - if type == "set" then - "${attribute} = { " + (makeArgList value) + " }" - else if elem type [ "string" "path" "bool" ] then - "${attribute} = ${if type == "bool" then boolToString value else ''"${value}"''}" - else if value == null then - "" - else - throw "Unsupported type '${type}' for attribute '${attribute}'!"; - - in - concatStringsSep ", " (mapAttrsToList makeArg set); - - - /* Recurses into the `nodeValue` attrset. Only subattrsets that - are JBoss paths, i.e. follows the `key=value` format, are recursed - into - the rest are considered JBoss attributes / maps. - */ - recurse = nodePath: nodeValue: - let - nodeContent = - if isAttrs nodeValue && nodeValue._type or "" == "order" then - nodeValue.content - else - nodeValue; - isPath = name: - let - value = nodeContent.${name}; - in - if (match ".*([=]).*" name) == [ "=" ] then - if isAttrs value || value == null then - true - else - throw "Parsing path '${concatStringsSep "." (nodePath ++ [ name ])}' failed: JBoss attributes cannot contain '='!" - else - false; - jbossPath = "/" + concatStringsSep "/" nodePath; - children = if !isAttrs nodeContent then { } else nodeContent; - subPaths = filter isPath (attrNames children); - getPriority = name: - let - value = children.${name}; - in - if value._type or "" == "order" then value.priority else 1000; - orderedSubPaths = sort (a: b: getPriority a < getPriority b) subPaths; - jbossAttrs = filterAttrs (name: _: !(isPath name)) children; - text = - if nodeContent != null then - '' - if (outcome != success) of ${jbossPath}:read-resource() - ${jbossPath}:add(${makeArgList jbossAttrs}) - end-if - '' + writeAttributes jbossPath jbossAttrs - else - '' - if (outcome == success) of ${jbossPath}:read-resource() - ${jbossPath}:remove() - end-if - ''; - in - text + concatMapStringsSep "\n" (name: recurse (nodePath ++ [ name ]) children.${name}) orderedSubPaths; - in - recurse [ ] attrs; - - jbossCliScript = pkgs.writeText "jboss-cli-script" (mkJbossScript keycloakConfig'); - - keycloakConfig = pkgs.runCommand "keycloak-config" - { - nativeBuildInputs = [ cfg.package ]; - } - '' - export JBOSS_BASE_DIR="$(pwd -P)"; - export JBOSS_MODULEPATH="${cfg.package}/modules"; - export JBOSS_LOG_DIR="$JBOSS_BASE_DIR/log"; - - cp -r ${cfg.package}/standalone/configuration . - chmod -R u+rwX ./configuration - - mkdir -p {deployments,ssl} - - standalone.sh& - - attempt=1 - max_attempts=30 - while ! jboss-cli.sh --connect ':read-attribute(name=server-state)'; do - if [[ "$attempt" == "$max_attempts" ]]; then - echo "ERROR: Could not connect to Keycloak after $attempt attempts! Failing.." >&2 - exit 1 - fi - echo "Keycloak not fully started yet, retrying.. ($attempt/$max_attempts)" - sleep 1 - (( attempt++ )) - done - - jboss-cli.sh --connect --file=${jbossCliScript} --echo-command + keycloakConfig = lib.generators.toKeyValue { + mkKeyValue = lib.flip lib.generators.mkKeyValueDefault "=" { + mkValueString = v: with builtins; + if isInt v then toString v + else if isString v then v + else if true == v then "true" + else if false == v then "false" + else if isSecret v then hashString "sha256" v._secret + else throw "unsupported type ${typeOf v}: ${(lib.generators.toPretty {}) v}"; + }; + }; - cp configuration/standalone.xml $out - ''; + isSecret = v: isAttrs v && v ? _secret && isString v._secret; + filteredConfig = lib.converge (lib.filterAttrsRecursive (_: v: ! elem v [{ } null])) cfg.settings; + confFile = pkgs.writeText "keycloak.conf" (keycloakConfig filteredConfig); + keycloakBuild = cfg.package.override { + inherit confFile; + plugins = cfg.package.enabledPlugins ++ cfg.plugins; + }; in mkIf cfg.enable { @@ -689,7 +504,46 @@ in } ]; - environment.systemPackages = [ cfg.package ]; + environment.systemPackages = [ keycloakBuild ]; + + services.keycloak.settings = + let + postgresParams = concatStringsSep "&" ( + optionals cfg.database.useSSL [ + "ssl=true" + ] ++ optionals (cfg.database.caCert != null) [ + "sslrootcert=${cfg.database.caCert}" + "sslmode=verify-ca" + ] + ); + mariadbParams = concatStringsSep "&" ([ + "characterEncoding=UTF-8" + ] ++ optionals cfg.database.useSSL [ + "useSSL=true" + "requireSSL=true" + "verifyServerCertificate=true" + ] ++ optionals (cfg.database.caCert != null) [ + "trustCertificateKeyStoreUrl=file:${mySqlCaKeystore}" + "trustCertificateKeyStorePassword=notsosecretpassword" + ]); + dbProps = if cfg.database.type == "postgresql" then postgresParams else mariadbParams; + in + mkMerge [ + { + db = if cfg.database.type == "postgresql" then "postgres" else cfg.database.type; + db-username = if databaseActuallyCreateLocally then "keycloak" else cfg.database.username; + db-password._secret = cfg.database.passwordFile; + db-url-host = cfg.database.host; + db-url-port = toString cfg.database.port; + db-url-database = if databaseActuallyCreateLocally then "keycloak" else cfg.database.name; + db-url-properties = prefixUnlessEmpty "?" dbProps; + db-url = null; + } + (mkIf (cfg.sslCertificate != null && cfg.sslCertificateKey != null) { + https-certificate-file = "/run/keycloak/ssl/ssl_cert"; + https-certificate-key-file = "/run/keycloak/ssl/ssl_key"; + }) + ]; systemd.services.keycloakPostgreSQLInit = mkIf createLocalPostgreSQL { after = [ "postgresql.service" ]; @@ -708,7 +562,7 @@ in shopt -s inherit_errexit create_role="$(mktemp)" - trap 'rm -f "$create_role"' ERR EXIT + trap 'rm -f "$create_role"' EXIT db_password="$(<"$CREDENTIALS_DIRECTORY/db_password")" echo "CREATE ROLE keycloak WITH LOGIN PASSWORD '$db_password' CREATEDB" > "$create_role" @@ -752,41 +606,37 @@ in "mysql.service" ] else [ ]; + secretPaths = catAttrs "_secret" (collect isSecret cfg.settings); + mkSecretReplacement = file: '' + replace-secret ${hashString "sha256" file} $CREDENTIALS_DIRECTORY/${baseNameOf file} /run/keycloak/conf/keycloak.conf + ''; + secretReplacements = lib.concatMapStrings mkSecretReplacement secretPaths; in { after = databaseServices; bindsTo = databaseServices; wantedBy = [ "multi-user.target" ]; path = with pkgs; [ - cfg.package + keycloakBuild openssl replace-secret ]; environment = { - JBOSS_LOG_DIR = "/var/log/keycloak"; - JBOSS_BASE_DIR = "/run/keycloak"; - JBOSS_MODULEPATH = "${cfg.package}/modules"; + KC_HOME_DIR = "/run/keycloak"; + KC_CONF_DIR = "/run/keycloak/conf"; }; serviceConfig = { - LoadCredential = [ - "db_password:${cfg.database.passwordFile}" - ] ++ optionals (cfg.sslCertificate != null && cfg.sslCertificateKey != null) [ - "ssl_cert:${cfg.sslCertificate}" - "ssl_key:${cfg.sslCertificateKey}" - ]; + LoadCredential = + map (p: "${baseNameOf p}:${p}") secretPaths + ++ optionals (cfg.sslCertificate != null && cfg.sslCertificateKey != null) [ + "ssl_cert:${cfg.sslCertificate}" + "ssl_key:${cfg.sslCertificateKey}" + ]; User = "keycloak"; Group = "keycloak"; DynamicUser = true; - RuntimeDirectory = map (p: "keycloak/" + p) [ - "configuration" - "deployments" - "data" - "ssl" - "log" - "tmp" - ]; + RuntimeDirectory = "keycloak"; RuntimeDirectoryMode = 0700; - LogsDirectory = "keycloak"; AmbientCapabilities = "CAP_NET_BIND_SERVICE"; }; script = '' @@ -795,41 +645,30 @@ in umask u=rwx,g=,o= - install_plugin() { - if [ -d "$1" ]; then - find "$1" -type f \( -iname \*.ear -o -iname \*.jar \) -exec install -m 0500 -o keycloak -g keycloak "{}" "/run/keycloak/deployments/" \; - else - install -m 0500 -o keycloak -g keycloak "$1" "/run/keycloak/deployments/" - fi - } - - install -m 0600 ${cfg.package}/standalone/configuration/*.properties /run/keycloak/configuration - install -T -m 0600 ${keycloakConfig} /run/keycloak/configuration/standalone.xml - - replace-secret '@db-password@' "$CREDENTIALS_DIRECTORY/db_password" /run/keycloak/configuration/standalone.xml - - export JAVA_OPTS=-Djboss.server.config.user.dir=/run/keycloak/configuration - add-user-keycloak.sh -u admin -p '${cfg.initialAdminPassword}' - '' - + lib.optionalString (cfg.plugins != []) (lib.concatStringsSep "\n" (map (pl: "install_plugin ${lib.escapeShellArg pl}") cfg.plugins)) + "\n" - + optionalString (cfg.sslCertificate != null && cfg.sslCertificateKey != null) '' - pushd /run/keycloak/ssl/ - cat "$CREDENTIALS_DIRECTORY/ssl_cert" <(echo) \ - "$CREDENTIALS_DIRECTORY/ssl_key" <(echo) \ - /etc/ssl/certs/ca-certificates.crt \ - > allcerts.pem - openssl pkcs12 -export -in "$CREDENTIALS_DIRECTORY/ssl_cert" -inkey "$CREDENTIALS_DIRECTORY/ssl_key" -chain \ - -name "${cfg.frontendUrl}" -out certificate_private_key_bundle.p12 \ - -CAfile allcerts.pem -passout pass:notsosecretpassword - popd + ln -s ${themesBundle} /run/keycloak/themes + ln -s ${keycloakBuild}/providers /run/keycloak/ + + install -D -m 0600 ${confFile} /run/keycloak/conf/keycloak.conf + + ${secretReplacements} + + '' + optionalString (cfg.sslCertificate != null && cfg.sslCertificateKey != null) '' + mkdir -p /run/keycloak/ssl + cp $CREDENTIALS_DIRECTORY/ssl_{cert,key} /run/keycloak/ssl/ '' + '' - ${cfg.package}/bin/standalone.sh + export KEYCLOAK_ADMIN=admin + export KEYCLOAK_ADMIN_PASSWORD=${cfg.initialAdminPassword} + kc.sh start ''; }; services.postgresql.enable = mkDefault createLocalPostgreSQL; services.mysql.enable = mkDefault createLocalMySQL; - services.mysql.package = mkIf createLocalMySQL pkgs.mariadb; + services.mysql.package = + let + dbPkg = if cfg.database.type == "mariadb" then pkgs.mariadb else pkgs.mysql80; + in + mkIf createLocalMySQL (mkDefault dbPkg); }; meta.doc = ./keycloak.xml; diff --git a/nixpkgs/nixos/modules/services/web-apps/keycloak.xml b/nixpkgs/nixos/modules/services/web-apps/keycloak.xml index cb706932f48f..861756e33ac0 100644 --- a/nixpkgs/nixos/modules/services/web-apps/keycloak.xml +++ b/nixpkgs/nixos/modules/services/web-apps/keycloak.xml @@ -27,10 +27,10 @@ <para> Refer to the <link - xlink:href="https://www.keycloak.org/docs/latest/server_admin/index.html#admin-console">Admin - Console section of the Keycloak Server Administration Guide</link> for - information on how to administer your - <productname>Keycloak</productname> instance. + xlink:href="https://www.keycloak.org/docs/latest/server_admin/index.html"> + Keycloak Server Administration Guide</link> for information on + how to administer your <productname>Keycloak</productname> + instance. </para> </section> @@ -38,27 +38,28 @@ <title>Database access</title> <para> <productname>Keycloak</productname> can be used with either - <productname>PostgreSQL</productname> or + <productname>PostgreSQL</productname>, + <productname>MariaDB</productname> or <productname>MySQL</productname>. Which one is used can be configured in <xref linkend="opt-services.keycloak.database.type" />. The selected database will automatically be enabled and a database and role created unless <xref - linkend="opt-services.keycloak.database.host" /> is changed from - its default of <literal>localhost</literal> or <xref - linkend="opt-services.keycloak.database.createLocally" /> is set - to <literal>false</literal>. + linkend="opt-services.keycloak.database.host" /> is changed + from its default of <literal>localhost</literal> or <xref + linkend="opt-services.keycloak.database.createLocally" /> is + set to <literal>false</literal>. </para> <para> External database access can also be configured by setting <xref linkend="opt-services.keycloak.database.host" />, <xref + linkend="opt-services.keycloak.database.name" />, <xref linkend="opt-services.keycloak.database.username" />, <xref linkend="opt-services.keycloak.database.useSSL" /> and <xref linkend="opt-services.keycloak.database.caCert" /> as - appropriate. Note that you need to manually create a database - called <literal>keycloak</literal> and allow the configured - database user full access to it. + appropriate. Note that you need to manually create the database + and allow the configured database user full access to it. </para> <para> @@ -79,22 +80,27 @@ </warning> </section> - <section xml:id="module-services-keycloak-frontendurl"> - <title>Frontend URL</title> + <section xml:id="module-services-keycloak-hostname"> + <title>Hostname</title> <para> - The frontend URL is used as base for all frontend requests and - must be configured through <xref linkend="opt-services.keycloak.frontendUrl" />. - It should normally include a trailing <literal>/auth</literal> - (the default web context). If you use a reverse proxy, you need - to set this option to <literal>""</literal>, so that frontend URL - is derived from HTTP headers. <literal>X-Forwarded-*</literal> headers - support also should be enabled, using <link - xlink:href="https://www.keycloak.org/docs/latest/server_installation/index.html#identifying-client-ip-addresses"> - respective guidelines</link>. + The hostname is used to build the public URL used as base for + all frontend requests and must be configured through <xref + linkend="opt-services.keycloak.settings.hostname" />. </para> + <note> + <para> + If you're migrating an old Wildfly based Keycloak instance + and want to keep compatibility with your current clients, + you'll likely want to set <xref + linkend="opt-services.keycloak.settings.http-relative-path" + /> to <literal>/auth</literal>. See the option description + for more details. + </para> + </note> + <para> - <xref linkend="opt-services.keycloak.forceBackendUrlToFrontendUrl" /> + <xref linkend="opt-services.keycloak.settings.hostname-strict-backchannel" /> determines whether Keycloak should force all requests to go through the frontend URL. By default, <productname>Keycloak</productname> allows backend requests to @@ -104,10 +110,10 @@ </para> <para> - See the <link - xlink:href="https://www.keycloak.org/docs/latest/server_installation/#_hostname">Hostname - section of the Keycloak Server Installation and Configuration - Guide</link> for more information. + For more information on hostname configuration, see the <link + xlink:href="https://www.keycloak.org/server/hostname">Hostname + section of the Keycloak Server Installation and Configuration + Guide</link>. </para> </section> @@ -139,68 +145,40 @@ <section xml:id="module-services-keycloak-themes"> <title>Themes</title> <para> - You can package custom themes and make them visible to Keycloak via - <xref linkend="opt-services.keycloak.themes" /> - option. See the <link xlink:href="https://www.keycloak.org/docs/latest/server_development/#_themes"> + You can package custom themes and make them visible to + Keycloak through <xref linkend="opt-services.keycloak.themes" + />. See the <link + xlink:href="https://www.keycloak.org/docs/latest/server_development/#_themes"> Themes section of the Keycloak Server Development Guide</link> - and respective NixOS option description for more information. + and the description of the aforementioned NixOS option for + more information. </para> </section> - <section xml:id="module-services-keycloak-extra-config"> - <title>Additional configuration</title> + <section xml:id="module-services-keycloak-settings"> + <title>Configuration file settings</title> <para> - Additional Keycloak configuration options, for which no - explicit <productname>NixOS</productname> options are provided, - can be set in <xref linkend="opt-services.keycloak.extraConfig" />. + Keycloak server configuration parameters can be set in <xref + linkend="opt-services.keycloak.settings" />. These correspond + directly to options in + <filename>conf/keycloak.conf</filename>. Some of the most + important parameters are documented as suboptions, the rest can + be found in the <link + xlink:href="https://www.keycloak.org/server/all-config">All + configuration section of the Keycloak Server Installation and + Configuration Guide</link>. </para> <para> - Options are expressed as a Nix attribute set which matches the - structure of the jboss-cli configuration. The configuration is - effectively overlayed on top of the default configuration - shipped with Keycloak. To remove existing nodes and undefine - attributes from the default configuration, set them to - <literal>null</literal>. - </para> - <para> - For example, the following script, which removes the hostname - provider <literal>default</literal>, adds the deprecated - hostname provider <literal>fixed</literal> and defines it the - default: - -<programlisting> -/subsystem=keycloak-server/spi=hostname/provider=default:remove() -/subsystem=keycloak-server/spi=hostname/provider=fixed:add(enabled = true, properties = { hostname = "keycloak.example.com" }) -/subsystem=keycloak-server/spi=hostname:write-attribute(name=default-provider, value="fixed") -</programlisting> - - would be expressed as - -<programlisting> -services.keycloak.extraConfig = { - "subsystem=keycloak-server" = { - "spi=hostname" = { - "provider=default" = null; - "provider=fixed" = { - enabled = true; - properties.hostname = "keycloak.example.com"; - }; - default-provider = "fixed"; - }; - }; -}; -</programlisting> - </para> - <para> - You can discover available options by using the <link - xlink:href="http://docs.wildfly.org/21/Admin_Guide.html#Command_Line_Interface">jboss-cli.sh</link> - program and by referring to the <link - xlink:href="https://www.keycloak.org/docs/latest/server_installation/index.html">Keycloak - Server Installation and Configuration Guide</link>. + Options containing secret data should be set to an attribute + set containing the attribute <literal>_secret</literal> - a + string pointing to a file containing the value the option + should be set to. See the description of <xref + linkend="opt-services.keycloak.settings" /> for an example. </para> </section> + <section xml:id="module-services-keycloak-example-config"> <title>Example configuration</title> <para> @@ -208,9 +186,11 @@ services.keycloak.extraConfig = { <programlisting> services.keycloak = { <link linkend="opt-services.keycloak.enable">enable</link> = true; + settings = { + <link linkend="opt-services.keycloak.settings.hostname">hostname</link> = "keycloak.example.com"; + <link linkend="opt-services.keycloak.settings.hostname-strict-backchannel">hostname-strict-backchannel</link> = true; + }; <link linkend="opt-services.keycloak.initialAdminPassword">initialAdminPassword</link> = "e6Wcm0RrtegMEHl"; # change on first login - <link linkend="opt-services.keycloak.frontendUrl">frontendUrl</link> = "https://keycloak.example.com/auth"; - <link linkend="opt-services.keycloak.forceBackendUrlToFrontendUrl">forceBackendUrlToFrontendUrl</link> = true; <link linkend="opt-services.keycloak.sslCertificate">sslCertificate</link> = "/run/keys/ssl_cert"; <link linkend="opt-services.keycloak.sslCertificateKey">sslCertificateKey</link> = "/run/keys/ssl_key"; <link linkend="opt-services.keycloak.database.passwordFile">database.passwordFile</link> = "/run/keys/db_password"; diff --git a/nixpkgs/nixos/modules/services/web-apps/komga.nix b/nixpkgs/nixos/modules/services/web-apps/komga.nix new file mode 100644 index 000000000000..a2809e64a9c3 --- /dev/null +++ b/nixpkgs/nixos/modules/services/web-apps/komga.nix @@ -0,0 +1,99 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.komga; + +in { + options = { + services.komga = { + enable = mkEnableOption "Komga, a free and open source comics/mangas media server"; + + port = mkOption { + type = types.port; + default = 8080; + description = lib.mdDoc '' + The port that Komga will listen on. + ''; + }; + + user = mkOption { + type = types.str; + default = "komga"; + description = lib.mdDoc '' + User account under which Komga runs. + ''; + }; + + group = mkOption { + type = types.str; + default = "komga"; + description = lib.mdDoc '' + Group under which Komga runs. + ''; + }; + + stateDir = mkOption { + type = types.str; + default = "/var/lib/komga"; + description = lib.mdDoc '' + State and configuration directory Komga will use. + ''; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Whether to open the firewall for the port in {option}`services.komga.port`. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + + networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ]; + + users.groups = mkIf (cfg.group == "komga") { + komga = {}; + }; + + users.users = mkIf (cfg.user == "komga") { + komga = { + group = cfg.group; + home = cfg.stateDir; + description = "Komga Daemon user"; + isSystemUser = true; + }; + }; + + systemd.services.komga = { + environment = { + SERVER_PORT = builtins.toString cfg.port; + KOMGA_CONFIGDIR = cfg.stateDir; + }; + + description = "Komga is a free and open source comics/mangas media server"; + + wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; + after = [ "network-online.target" ]; + + serviceConfig = { + User = cfg.user; + Group = cfg.group; + + Type = "simple"; + Restart = "on-failure"; + ExecStart = "${pkgs.komga}/bin/komga"; + + StateDirectory = mkIf (cfg.stateDir == "/var/lib/komga") "komga"; + }; + + }; + }; + + meta.maintainers = with maintainers; [ govanify ]; +} diff --git a/nixpkgs/nixos/modules/services/web-apps/lemmy.nix b/nixpkgs/nixos/modules/services/web-apps/lemmy.nix index 7cd2357c4556..3e726149e93d 100644 --- a/nixpkgs/nixos/modules/services/web-apps/lemmy.nix +++ b/nixpkgs/nixos/modules/services/web-apps/lemmy.nix @@ -16,14 +16,14 @@ in jwtSecretPath = mkOption { type = types.path; - description = "Path to read the jwt secret from."; + description = lib.mdDoc "Path to read the jwt secret from."; }; ui = { port = mkOption { type = types.port; default = 1234; - description = "Port where lemmy-ui should listen for incoming requests."; + description = lib.mdDoc "Port where lemmy-ui should listen for incoming requests."; }; }; @@ -31,7 +31,7 @@ in settings = mkOption { default = { }; - description = "Lemmy configuration"; + description = lib.mdDoc "Lemmy configuration"; type = types.submodule { freeformType = settingsFormat.type; @@ -39,13 +39,13 @@ in options.hostname = mkOption { type = types.str; default = null; - description = "The domain name of your instance (eg 'lemmy.ml')."; + description = lib.mdDoc "The domain name of your instance (eg 'lemmy.ml')."; }; options.port = mkOption { type = types.port; default = 8536; - description = "Port where lemmy should listen for incoming requests."; + description = lib.mdDoc "Port where lemmy should listen for incoming requests."; }; options.federation = { @@ -56,12 +56,12 @@ in enabled = mkOption { type = types.bool; default = true; - description = "Enable Captcha."; + description = lib.mdDoc "Enable Captcha."; }; difficulty = mkOption { type = types.enum [ "easy" "medium" "hard" ]; default = "medium"; - description = "The difficultly of the captcha to solve."; + description = lib.mdDoc "The difficultly of the captcha to solve."; }; }; @@ -164,7 +164,7 @@ in wantedBy = [ "multi-user.target" ]; - after = [ "pict-rs.service " ] ++ lib.optionals cfg.settings.database.createLocally [ "lemmy-postgresql.service" ]; + after = [ "pict-rs.service" ] ++ lib.optionals cfg.settings.database.createLocally [ "lemmy-postgresql.service" ]; requires = lib.optionals cfg.settings.database.createLocally [ "lemmy-postgresql.service" ]; diff --git a/nixpkgs/nixos/modules/services/web-apps/limesurvey.nix b/nixpkgs/nixos/modules/services/web-apps/limesurvey.nix index 5ccd742a303b..e0995e0b5a44 100644 --- a/nixpkgs/nixos/modules/services/web-apps/limesurvey.nix +++ b/nixpkgs/nixos/modules/services/web-apps/limesurvey.nix @@ -39,41 +39,41 @@ in type = types.enum [ "mysql" "pgsql" "odbc" "mssql" ]; example = "pgsql"; default = "mysql"; - description = "Database engine to use."; + description = lib.mdDoc "Database engine to use."; }; host = mkOption { type = types.str; default = "localhost"; - description = "Database host address."; + description = lib.mdDoc "Database host address."; }; port = mkOption { type = types.int; default = if cfg.database.type == "pgsql" then 5442 else 3306; defaultText = literalExpression "3306"; - description = "Database host port."; + description = lib.mdDoc "Database host port."; }; name = mkOption { type = types.str; default = "limesurvey"; - description = "Database name."; + description = lib.mdDoc "Database name."; }; user = mkOption { type = types.str; default = "limesurvey"; - description = "Database user."; + description = lib.mdDoc "Database user."; }; passwordFile = mkOption { type = types.nullOr types.path; default = null; example = "/run/keys/limesurvey-dbpassword"; - description = '' + description = lib.mdDoc '' A file containing the password corresponding to - <option>database.user</option>. + {option}`database.user`. ''; }; @@ -85,14 +85,14 @@ in else null ; defaultText = literalExpression "/run/mysqld/mysqld.sock"; - description = "Path to the unix socket file to use for authentication."; + description = lib.mdDoc "Path to the unix socket file to use for authentication."; }; createLocally = mkOption { type = types.bool; default = cfg.database.type == "mysql"; defaultText = literalExpression "true"; - description = '' + description = lib.mdDoc '' Create the database and database user locally. This currently only applies if database type "mysql" is selected. ''; @@ -109,9 +109,9 @@ in enableACME = true; } ''; - description = '' - Apache configuration can be done by adapting <literal>services.httpd.virtualHosts.<name></literal>. - See <xref linkend="opt-services.httpd.virtualHosts"/> for further information. + description = lib.mdDoc '' + Apache configuration can be done by adapting `services.httpd.virtualHosts.<name>`. + See [](#opt-services.httpd.virtualHosts) for further information. ''; }; @@ -125,8 +125,8 @@ in "pm.max_spare_servers" = 4; "pm.max_requests" = 500; }; - description = '' - Options for the LimeSurvey PHP pool. See the documentation on <literal>php-fpm.conf</literal> + description = lib.mdDoc '' + Options for the LimeSurvey PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives. ''; }; @@ -134,9 +134,9 @@ in config = mkOption { type = configType; default = {}; - description = '' + description = lib.mdDoc '' LimeSurvey configuration. Refer to - <link xlink:href="https://manual.limesurvey.org/Optional_settings"/> + <https://manual.limesurvey.org/Optional_settings> for details on supported values. ''; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/mastodon.nix b/nixpkgs/nixos/modules/services/web-apps/mastodon.nix index 8208c85bfd70..d0594ff74192 100644 --- a/nixpkgs/nixos/modules/services/web-apps/mastodon.nix +++ b/nixpkgs/nixos/modules/services/web-apps/mastodon.nix @@ -9,6 +9,8 @@ let RAILS_ENV = "production"; NODE_ENV = "production"; + LD_PRELOAD = "${pkgs.jemalloc}/lib/libjemalloc.so"; + # mastodon-web concurrency. WEB_CONCURRENCY = toString cfg.webProcesses; MAX_THREADS = toString cfg.webThreads; @@ -111,17 +113,17 @@ in { affect other virtualHosts running on your nginx instance, if any. Alternatively you can configure a reverse-proxy of your choice to serve these paths: - <code>/ -> $(nix-instantiate --eval '<nixpkgs>' -A mastodon.outPath)/public</code> + <literal>/ -> $(nix-instantiate --eval '<nixpkgs>' -A mastodon.outPath)/public</literal> - <code>/ -> 127.0.0.1:{{ webPort }} </code>(If there was no file in the directory above.) + <literal>/ -> 127.0.0.1:{{ webPort }} </literal>(If there was no file in the directory above.) - <code>/system/ -> /var/lib/mastodon/public-system/</code> + <literal>/system/ -> /var/lib/mastodon/public-system/</literal> - <code>/api/v1/streaming/ -> 127.0.0.1:{{ streamingPort }}</code> + <literal>/api/v1/streaming/ -> 127.0.0.1:{{ streamingPort }}</literal> Make sure that websockets are forwarded properly. You might want to set up caching of some requests. Take a look at mastodon's provided nginx configuration at - <code>https://github.com/tootsuite/mastodon/blob/master/dist/nginx.conf</code>. + <literal>https://github.com/mastodon/mastodon/blob/master/dist/nginx.conf</literal>. ''; type = lib.types.bool; default = false; @@ -133,20 +135,20 @@ in { that user will be created, otherwise it should be set to the name of a user created elsewhere. In both cases, <package>mastodon</package> and a package containing only - the shell script <code>mastodon-env</code> will be added to + the shell script <literal>mastodon-env</literal> will be added to the user's package set. To run a command from - <package>mastodon</package> such as <code>tootctl</code> + <package>mastodon</package> such as <literal>tootctl</literal> with the environment configured by this module use - <code>mastodon-env</code>, as in: + <literal>mastodon-env</literal>, as in: - <code>mastodon-env tootctl accounts create newuser --email newuser@example.com</code> + <literal>mastodon-env tootctl accounts create newuser --email newuser@example.com</literal> ''; type = lib.types.str; default = "mastodon"; }; group = lib.mkOption { - description = '' + description = lib.mdDoc '' Group under which mastodon runs. ''; type = lib.types.str; @@ -154,12 +156,12 @@ in { }; streamingPort = lib.mkOption { - description = "TCP port used by the mastodon-streaming service."; + description = lib.mdDoc "TCP port used by the mastodon-streaming service."; type = lib.types.port; default = 55000; }; streamingProcesses = lib.mkOption { - description = '' + description = lib.mdDoc '' Processes used by the mastodon-streaming service. Defaults to the number of CPU cores minus one. ''; @@ -168,41 +170,41 @@ in { }; webPort = lib.mkOption { - description = "TCP port used by the mastodon-web service."; + description = lib.mdDoc "TCP port used by the mastodon-web service."; type = lib.types.port; default = 55001; }; webProcesses = lib.mkOption { - description = "Processes used by the mastodon-web service."; + description = lib.mdDoc "Processes used by the mastodon-web service."; type = lib.types.int; default = 2; }; webThreads = lib.mkOption { - description = "Threads per process used by the mastodon-web service."; + description = lib.mdDoc "Threads per process used by the mastodon-web service."; type = lib.types.int; default = 5; }; sidekiqPort = lib.mkOption { - description = "TCP port used by the mastodon-sidekiq service."; + description = lib.mdDoc "TCP port used by the mastodon-sidekiq service."; type = lib.types.port; default = 55002; }; sidekiqThreads = lib.mkOption { - description = "Worker threads used by the mastodon-sidekiq service."; + description = lib.mdDoc "Worker threads used by the mastodon-sidekiq service."; type = lib.types.int; default = 25; }; vapidPublicKeyFile = lib.mkOption { - description = '' + description = lib.mdDoc '' Path to file containing the public key used for Web Push Voluntary Application Server Identification. A new keypair can be generated by running: - <code>nix build -f '<nixpkgs>' mastodon; cd result; bin/rake webpush:generate_keys</code> + `nix build -f '<nixpkgs>' mastodon; cd result; bin/rake webpush:generate_keys` - If <option>mastodon.vapidPrivateKeyFile</option>does not + If {option}`mastodon.vapidPrivateKeyFile`does not exist, it and this file will be created with a new keypair. ''; default = "/var/lib/mastodon/secrets/vapid-public-key"; @@ -210,17 +212,17 @@ in { }; localDomain = lib.mkOption { - description = "The domain serving your Mastodon instance."; + description = lib.mdDoc "The domain serving your Mastodon instance."; example = "social.example.org"; type = lib.types.str; }; secretKeyBaseFile = lib.mkOption { - description = '' + description = lib.mdDoc '' Path to file containing the secret key base. A new secret key base can be generated by running: - <code>nix build -f '<nixpkgs>' mastodon; cd result; bin/rake secret</code> + `nix build -f '<nixpkgs>' mastodon; cd result; bin/rake secret` If this file does not exist, it will be created with a new secret key base. ''; @@ -229,11 +231,11 @@ in { }; otpSecretFile = lib.mkOption { - description = '' + description = lib.mdDoc '' Path to file containing the OTP secret. A new OTP secret can be generated by running: - <code>nix build -f '<nixpkgs>' mastodon; cd result; bin/rake secret</code> + `nix build -f '<nixpkgs>' mastodon; cd result; bin/rake secret` If this file does not exist, it will be created with a new OTP secret. ''; @@ -242,12 +244,12 @@ in { }; vapidPrivateKeyFile = lib.mkOption { - description = '' + description = lib.mdDoc '' Path to file containing the private key used for Web Push Voluntary Application Server Identification. A new keypair can be generated by running: - <code>nix build -f '<nixpkgs>' mastodon; cd result; bin/rake webpush:generate_keys</code> + `nix build -f '<nixpkgs>' mastodon; cd result; bin/rake webpush:generate_keys` If this file does not exist, it will be created with a new private key. @@ -257,7 +259,7 @@ in { }; trustedProxy = lib.mkOption { - description = '' + description = lib.mdDoc '' You need to set it to the IP from which your reverse proxy sends requests to Mastodon's web process, otherwise Mastodon will record the reverse proxy's own IP as the IP of all requests, which would be bad because IP addresses are used for important rate limits and security functions. @@ -267,7 +269,7 @@ in { }; enableUnixSocket = lib.mkOption { - description = '' + description = lib.mdDoc '' Instead of binding to an IP address like 127.0.0.1, you may bind to a Unix socket. This variable is process-specific, e.g. you need different values for every process, and it works for both web (Puma) processes and streaming API (Node.js) processes. @@ -278,27 +280,27 @@ in { redis = { createLocally = lib.mkOption { - description = "Configure local Redis server for Mastodon."; + description = lib.mdDoc "Configure local Redis server for Mastodon."; type = lib.types.bool; default = true; }; host = lib.mkOption { - description = "Redis host."; + description = lib.mdDoc "Redis host."; type = lib.types.str; default = "127.0.0.1"; }; port = lib.mkOption { - description = "Redis port."; + description = lib.mdDoc "Redis port."; type = lib.types.port; - default = 6379; + default = 31637; }; }; database = { createLocally = lib.mkOption { - description = "Configure local PostgreSQL database server for Mastodon."; + description = lib.mdDoc "Configure local PostgreSQL database server for Mastodon."; type = lib.types.bool; default = true; }; @@ -307,75 +309,75 @@ in { type = lib.types.str; default = "/run/postgresql"; example = "192.168.23.42"; - description = "Database host address or unix socket."; + description = lib.mdDoc "Database host address or unix socket."; }; port = lib.mkOption { type = lib.types.int; default = 5432; - description = "Database host port."; + description = lib.mdDoc "Database host port."; }; name = lib.mkOption { type = lib.types.str; default = "mastodon"; - description = "Database name."; + description = lib.mdDoc "Database name."; }; user = lib.mkOption { type = lib.types.str; default = "mastodon"; - description = "Database user."; + description = lib.mdDoc "Database user."; }; passwordFile = lib.mkOption { type = lib.types.nullOr lib.types.path; default = "/var/lib/mastodon/secrets/db-password"; example = "/run/keys/mastodon-db-password"; - description = '' + description = lib.mdDoc '' A file containing the password corresponding to - <option>database.user</option>. + {option}`database.user`. ''; }; }; smtp = { createLocally = lib.mkOption { - description = "Configure local Postfix SMTP server for Mastodon."; + description = lib.mdDoc "Configure local Postfix SMTP server for Mastodon."; type = lib.types.bool; default = true; }; authenticate = lib.mkOption { - description = "Authenticate with the SMTP server using username and password."; + description = lib.mdDoc "Authenticate with the SMTP server using username and password."; type = lib.types.bool; default = false; }; host = lib.mkOption { - description = "SMTP host used when sending emails to users."; + description = lib.mdDoc "SMTP host used when sending emails to users."; type = lib.types.str; default = "127.0.0.1"; }; port = lib.mkOption { - description = "SMTP port used when sending emails to users."; + description = lib.mdDoc "SMTP port used when sending emails to users."; type = lib.types.port; default = 25; }; fromAddress = lib.mkOption { - description = ''"From" address used when sending Emails to users.''; + description = lib.mdDoc ''"From" address used when sending Emails to users.''; type = lib.types.str; }; user = lib.mkOption { - description = "SMTP login name."; + description = lib.mdDoc "SMTP login name."; type = lib.types.str; }; passwordFile = lib.mkOption { - description = '' + description = lib.mdDoc '' Path to file containing the SMTP password. ''; default = "/var/lib/mastodon/secrets/smtp-password"; @@ -386,7 +388,7 @@ in { elasticsearch = { host = lib.mkOption { - description = '' + description = lib.mdDoc '' Elasticsearch host. If it is not null, Elasticsearch full text search will be enabled. ''; @@ -395,7 +397,7 @@ in { }; port = lib.mkOption { - description = "Elasticsearch port."; + description = lib.mdDoc "Elasticsearch port."; type = lib.types.port; default = 9200; }; @@ -405,13 +407,13 @@ in { type = lib.types.package; default = pkgs.mastodon; defaultText = lib.literalExpression "pkgs.mastodon"; - description = "Mastodon package to use."; + description = lib.mdDoc "Mastodon package to use."; }; extraConfig = lib.mkOption { type = lib.types.attrs; default = {}; - description = '' + description = lib.mdDoc '' Extra environment variables to pass to all mastodon services. ''; }; @@ -419,7 +421,7 @@ in { automaticMigrations = lib.mkOption { type = lib.types.bool; default = true; - description = '' + description = lib.mdDoc '' Do automatic database migrations. ''; }; @@ -603,8 +605,10 @@ in { enable = true; hostname = lib.mkDefault "${cfg.localDomain}"; }; - services.redis = lib.mkIf (cfg.redis.createLocally && cfg.redis.host == "127.0.0.1") { + services.redis.servers.mastodon = lib.mkIf (cfg.redis.createLocally && cfg.redis.host == "127.0.0.1") { enable = true; + port = cfg.redis.port; + bind = "127.0.0.1"; }; services.postgresql = lib.mkIf databaseActuallyCreateLocally { enable = true; diff --git a/nixpkgs/nixos/modules/services/web-apps/matomo.nix b/nixpkgs/nixos/modules/services/web-apps/matomo.nix index c6d4ed6d39de..80c4db1263e1 100644 --- a/nixpkgs/nixos/modules/services/web-apps/matomo.nix +++ b/nixpkgs/nixos/modules/services/web-apps/matomo.nix @@ -32,7 +32,7 @@ in { enable = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Enable Matomo web analytics with php-fpm backend. Either the nginx option or the webServerUser option is mandatory. ''; @@ -40,7 +40,7 @@ in { package = mkOption { type = types.package; - description = '' + description = lib.mdDoc '' Matomo package for the service to use. This can be used to point to newer releases from nixos-unstable, as they don't get backported if they are not security-relevant. @@ -64,13 +64,13 @@ in { periodicArchiveProcessing = mkOption { type = types.bool; default = true; - description = '' + description = lib.mdDoc '' Enable periodic archive processing, which generates aggregated reports from the visits. This means that you can safely disable browser triggers for Matomo archiving, and safely enable to delete old visitor logs. Before deleting visitor logs, - make sure though that you run <literal>systemctl start matomo-archive-processing.service</literal> + make sure though that you run `systemctl start matomo-archive-processing.service` at least once without errors if you have already collected data before. ''; }; @@ -84,7 +84,7 @@ in { else "${user}.''${config.${options.networking.hostName}}" ''; example = "matomo.yourdomain.org"; - description = '' + description = lib.mdDoc '' URL of the host, without https prefix. You may want to change it if you run Matomo on a different URL than matomo.yourdomain. ''; @@ -112,12 +112,12 @@ in { enableACME = false; } ''; - description = '' + description = lib.mdDoc '' With this option, you can customize an nginx virtualHost which already has sensible defaults for Matomo. Either this option or the webServerUser option is mandatory. Set this to {} to just enable the virtualHost if you don't need any customization. - If enabled, then by default, the <option>serverName</option> is - <literal>''${user}.''${config.networking.hostName}.''${config.networking.domain}</literal>, + If enabled, then by default, the {option}`serverName` is + `''${user}.''${config.networking.hostName}.''${config.networking.domain}`, SSL is active, and certificates are acquired via ACME. If this is set to null (the default), no nginx virtualHost will be configured. ''; diff --git a/nixpkgs/nixos/modules/services/web-apps/mattermost.nix b/nixpkgs/nixos/modules/services/web-apps/mattermost.nix index 2901f307dc5a..6e9e2abcaa8c 100644 --- a/nixpkgs/nixos/modules/services/web-apps/mattermost.nix +++ b/nixpkgs/nixos/modules/services/web-apps/mattermost.nix @@ -107,19 +107,19 @@ in type = types.package; default = pkgs.mattermost; defaultText = "pkgs.mattermost"; - description = "Mattermost derivation to use."; + description = lib.mdDoc "Mattermost derivation to use."; }; statePath = mkOption { type = types.str; default = "/var/lib/mattermost"; - description = "Mattermost working directory"; + description = lib.mdDoc "Mattermost working directory"; }; siteUrl = mkOption { type = types.str; example = "https://chat.example.com"; - description = '' + description = lib.mdDoc '' URL this Mattermost instance is reachable under, without trailing slash. ''; }; @@ -127,14 +127,14 @@ in siteName = mkOption { type = types.str; default = "Mattermost"; - description = "Name of this Mattermost site."; + description = lib.mdDoc "Name of this Mattermost site."; }; listenAddress = mkOption { type = types.str; default = ":8065"; example = "[::1]:8065"; - description = '' + description = lib.mdDoc '' Address and port this Mattermost instance listens to. ''; }; @@ -142,7 +142,7 @@ in mutableConfig = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Whether the Mattermost config.json is writeable by Mattermost. Most of the settings can be edited in the system console of @@ -159,7 +159,7 @@ in preferNixConfig = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' If both mutableConfig and this option are set, the Nix configuration will take precedence over any settings configured in the server console. @@ -169,7 +169,7 @@ in extraConfig = mkOption { type = types.attrs; default = { }; - description = '' + description = lib.mdDoc '' Addtional configuration options as Nix attribute set in config.json schema. ''; }; @@ -178,7 +178,7 @@ in type = types.listOf (types.oneOf [types.path types.package]); default = []; example = "[ ./com.github.moussetc.mattermost.plugin.giphy-2.0.0.tar.gz ]"; - description = '' + description = lib.mdDoc '' Plugins to add to the configuration. Overrides any installed if non-null. This is a list of paths to .tar.gz files or derivations evaluating to .tar.gz files. @@ -188,7 +188,7 @@ in localDatabaseCreate = mkOption { type = types.bool; default = true; - description = '' + description = lib.mdDoc '' Create a local PostgreSQL database for Mattermost automatically. ''; }; @@ -196,7 +196,7 @@ in localDatabaseName = mkOption { type = types.str; default = "mattermost"; - description = '' + description = lib.mdDoc '' Local Mattermost database name. ''; }; @@ -204,7 +204,7 @@ in localDatabaseUser = mkOption { type = types.str; default = "mattermost"; - description = '' + description = lib.mdDoc '' Local Mattermost database username. ''; }; @@ -212,7 +212,7 @@ in localDatabasePassword = mkOption { type = types.str; default = "mmpgsecret"; - description = '' + description = lib.mdDoc '' Password for local Mattermost database user. ''; }; @@ -220,7 +220,7 @@ in user = mkOption { type = types.str; default = "mattermost"; - description = '' + description = lib.mdDoc '' User which runs the Mattermost service. ''; }; @@ -228,7 +228,7 @@ in group = mkOption { type = types.str; default = "mattermost"; - description = '' + description = lib.mdDoc '' Group which runs the Mattermost service. ''; }; @@ -239,13 +239,13 @@ in type = types.package; default = pkgs.matterircd; defaultText = "pkgs.matterircd"; - description = "matterircd derivation to use."; + description = lib.mdDoc "matterircd derivation to use."; }; parameters = mkOption { type = types.listOf types.str; default = [ ]; example = [ "-mmserver chat.example.com" "-bind [::]:6667" ]; - description = '' + description = lib.mdDoc '' Set commandline parameters to pass to matterircd. See https://github.com/42wim/matterircd#usage for more information. ''; diff --git a/nixpkgs/nixos/modules/services/web-apps/mediawiki.nix b/nixpkgs/nixos/modules/services/web-apps/mediawiki.nix index 977b6f60b230..01083eff612b 100644 --- a/nixpkgs/nixos/modules/services/web-apps/mediawiki.nix +++ b/nixpkgs/nixos/modules/services/web-apps/mediawiki.nix @@ -177,20 +177,20 @@ in type = types.package; default = pkgs.mediawiki; defaultText = literalExpression "pkgs.mediawiki"; - description = "Which MediaWiki package to use."; + description = lib.mdDoc "Which MediaWiki package to use."; }; name = mkOption { type = types.str; default = "MediaWiki"; example = "Foobar Wiki"; - description = "Name of the wiki."; + description = lib.mdDoc "Name of the wiki."; }; uploadsDir = mkOption { type = types.nullOr types.path; default = "${stateDir}/uploads"; - description = '' + description = lib.mdDoc '' This directory is used for uploads of pictures. The directory passed here is automatically created and permissions adjusted as required. ''; @@ -198,15 +198,15 @@ in passwordFile = mkOption { type = types.path; - description = "A file containing the initial password for the admin user."; + description = lib.mdDoc "A file containing the initial password for the admin user."; example = "/run/keys/mediawiki-password"; }; skins = mkOption { default = {}; type = types.attrsOf types.path; - description = '' - Attribute set of paths whose content is copied to the <filename>skins</filename> + description = lib.mdDoc '' + Attribute set of paths whose content is copied to the {file}`skins` subdirectory of the MediaWiki installation in addition to the default skins. ''; }; @@ -214,11 +214,11 @@ in extensions = mkOption { default = {}; type = types.attrsOf (types.nullOr types.path); - description = '' - Attribute set of paths whose content is copied to the <filename>extensions</filename> + description = lib.mdDoc '' + Attribute set of paths whose content is copied to the {file}`extensions` subdirectory of the MediaWiki installation and enabled in configuration. - Use <literal>null</literal> instead of path to enable extensions that are part of MediaWiki. + Use `null` instead of path to enable extensions that are part of MediaWiki. ''; example = literalExpression '' { @@ -235,52 +235,52 @@ in type = mkOption { type = types.enum [ "mysql" "postgres" "sqlite" "mssql" "oracle" ]; default = "mysql"; - description = "Database engine to use. MySQL/MariaDB is the database of choice by MediaWiki developers."; + description = lib.mdDoc "Database engine to use. MySQL/MariaDB is the database of choice by MediaWiki developers."; }; host = mkOption { type = types.str; default = "localhost"; - description = "Database host address."; + description = lib.mdDoc "Database host address."; }; port = mkOption { type = types.port; default = 3306; - description = "Database host port."; + description = lib.mdDoc "Database host port."; }; name = mkOption { type = types.str; default = "mediawiki"; - description = "Database name."; + description = lib.mdDoc "Database name."; }; user = mkOption { type = types.str; default = "mediawiki"; - description = "Database user."; + description = lib.mdDoc "Database user."; }; passwordFile = mkOption { type = types.nullOr types.path; default = null; example = "/run/keys/mediawiki-dbpassword"; - description = '' + description = lib.mdDoc '' A file containing the password corresponding to - <option>database.user</option>. + {option}`database.user`. ''; }; tablePrefix = mkOption { type = types.nullOr types.str; default = null; - description = '' + description = lib.mdDoc '' 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 <link xlink:href='https://www.mediawiki.org/wiki/Manual:$wgDBprefix'/>. + See <https://www.mediawiki.org/wiki/Manual:$wgDBprefix>. ''; }; @@ -288,14 +288,14 @@ in type = types.nullOr types.path; default = if cfg.database.createLocally then "/run/mysqld/mysqld.sock" else null; defaultText = literalExpression "/run/mysqld/mysqld.sock"; - description = "Path to the unix socket file to use for authentication."; + description = lib.mdDoc "Path to the unix socket file to use for authentication."; }; createLocally = mkOption { type = types.bool; default = cfg.database.type == "mysql"; defaultText = literalExpression "true"; - description = '' + description = lib.mdDoc '' Create the database and database user locally. This currently only applies if database type "mysql" is selected. ''; @@ -312,9 +312,9 @@ in enableACME = true; } ''; - description = '' - Apache configuration can be done by adapting <option>services.httpd.virtualHosts</option>. - See <xref linkend="opt-services.httpd.virtualHosts"/> for further information. + description = lib.mdDoc '' + Apache configuration can be done by adapting {option}`services.httpd.virtualHosts`. + See [](#opt-services.httpd.virtualHosts) for further information. ''; }; @@ -328,18 +328,18 @@ in "pm.max_spare_servers" = 4; "pm.max_requests" = 500; }; - description = '' - Options for the MediaWiki PHP pool. See the documentation on <literal>php-fpm.conf</literal> + description = lib.mdDoc '' + Options for the MediaWiki PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives. ''; }; extraConfig = mkOption { type = types.lines; - description = '' + description = lib.mdDoc '' Any additional text to be appended to MediaWiki's LocalSettings.php configuration file. For configuration - settings, see <link xlink:href="https://www.mediawiki.org/wiki/Manual:Configuration_settings"/>. + settings, see <https://www.mediawiki.org/wiki/Manual:Configuration_settings>. ''; default = ""; example = '' diff --git a/nixpkgs/nixos/modules/services/web-apps/miniflux.nix b/nixpkgs/nixos/modules/services/web-apps/miniflux.nix index 641c9be85d8c..55e3664bee68 100644 --- a/nixpkgs/nixos/modules/services/web-apps/miniflux.nix +++ b/nixpkgs/nixos/modules/services/web-apps/miniflux.nix @@ -29,9 +29,9 @@ in LISTEN_ADDR = "localhost:8080"; } ''; - description = '' + description = lib.mdDoc '' Configuration for Miniflux, refer to - <link xlink:href="https://miniflux.app/docs/configuration.html"/> + <https://miniflux.app/docs/configuration.html> for documentation on the supported values. Correct configuration for the database is already provided. @@ -41,7 +41,7 @@ in adminCredentialsFile = mkOption { type = types.path; - description = '' + description = lib.mdDoc '' File containing the ADMIN_USERNAME and ADMIN_PASSWORD (length >= 6) in the format of an EnvironmentFile=, as described by systemd.exec(5). diff --git a/nixpkgs/nixos/modules/services/web-apps/moodle.nix b/nixpkgs/nixos/modules/services/web-apps/moodle.nix index 19f3e754691e..6d1a9839ca1f 100644 --- a/nixpkgs/nixos/modules/services/web-apps/moodle.nix +++ b/nixpkgs/nixos/modules/services/web-apps/moodle.nix @@ -56,7 +56,7 @@ let mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql"; pgsqlLocal = cfg.database.createLocally && cfg.database.type == "pgsql"; - phpExt = pkgs.php74.withExtensions + phpExt = pkgs.php81.withExtensions ({ enabled, all }: with all; [ iconv mbstring curl openssl tokenizer xmlrpc soap ctype zip gd simplexml dom intl json sqlite3 pgsql pdo_sqlite pdo_pgsql pdo_odbc pdo_mysql pdo mysqli session zlib xmlreader fileinfo filter opcache ]); in { @@ -68,13 +68,13 @@ in type = types.package; default = pkgs.moodle; defaultText = literalExpression "pkgs.moodle"; - description = "The Moodle package to use."; + description = lib.mdDoc "The Moodle package to use."; }; initialPassword = mkOption { type = types.str; example = "correcthorsebatterystaple"; - description = '' + description = lib.mdDoc '' Specifies the initial password for the admin, i.e. the password assigned if the user does not already exist. The password specified here is world-readable in the Nix store, so it should be changed promptly. ''; @@ -84,18 +84,18 @@ in type = mkOption { type = types.enum [ "mysql" "pgsql" ]; default = "mysql"; - description = "Database engine to use."; + description = lib.mdDoc "Database engine to use."; }; host = mkOption { type = types.str; default = "localhost"; - description = "Database host address."; + description = lib.mdDoc "Database host address."; }; port = mkOption { type = types.int; - description = "Database host port."; + description = lib.mdDoc "Database host port."; default = { mysql = 3306; pgsql = 5432; @@ -106,22 +106,22 @@ in name = mkOption { type = types.str; default = "moodle"; - description = "Database name."; + description = lib.mdDoc "Database name."; }; user = mkOption { type = types.str; default = "moodle"; - description = "Database user."; + description = lib.mdDoc "Database user."; }; passwordFile = mkOption { type = types.nullOr types.path; default = null; example = "/run/keys/moodle-dbpassword"; - description = '' + description = lib.mdDoc '' A file containing the password corresponding to - <option>database.user</option>. + {option}`database.user`. ''; }; @@ -132,7 +132,7 @@ in else if pgsqlLocal then "/run/postgresql" else null; defaultText = literalExpression "/run/mysqld/mysqld.sock"; - description = "Path to the unix socket file to use for authentication."; + description = lib.mdDoc "Path to the unix socket file to use for authentication."; }; createLocally = mkOption { @@ -152,9 +152,9 @@ in enableACME = true; } ''; - description = '' - Apache configuration can be done by adapting <option>services.httpd.virtualHosts</option>. - See <xref linkend="opt-services.httpd.virtualHosts"/> for further information. + description = lib.mdDoc '' + Apache configuration can be done by adapting {option}`services.httpd.virtualHosts`. + See [](#opt-services.httpd.virtualHosts) for further information. ''; }; @@ -168,8 +168,8 @@ in "pm.max_spare_servers" = 4; "pm.max_requests" = 500; }; - description = '' - Options for the Moodle PHP pool. See the documentation on <literal>php-fpm.conf</literal> + description = lib.mdDoc '' + Options for the Moodle PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives. ''; }; @@ -177,10 +177,10 @@ in extraConfig = mkOption { type = types.lines; default = ""; - description = '' + description = lib.mdDoc '' Any additional text to be appended to the config.php configuration file. This is a PHP script. For configuration - details, see <link xlink:href="https://docs.moodle.org/37/en/Configuration_file"/>. + details, see <https://docs.moodle.org/37/en/Configuration_file>. ''; example = '' $CFG->disableupdatenotifications = true; diff --git a/nixpkgs/nixos/modules/services/web-apps/netbox.nix b/nixpkgs/nixos/modules/services/web-apps/netbox.nix new file mode 100644 index 000000000000..2826e57f2c77 --- /dev/null +++ b/nixpkgs/nixos/modules/services/web-apps/netbox.nix @@ -0,0 +1,265 @@ +{ config, lib, pkgs, buildEnv, ... }: + +with lib; + +let + cfg = config.services.netbox; + staticDir = cfg.dataDir + "/static"; + configFile = pkgs.writeTextFile { + name = "configuration.py"; + text = '' + STATIC_ROOT = '${staticDir}' + ALLOWED_HOSTS = ['*'] + DATABASE = { + 'NAME': 'netbox', + 'USER': 'netbox', + 'HOST': '/run/postgresql', + } + + # Redis database settings. Redis is used for caching and for queuing background tasks such as webhook events. A separate + # configuration exists for each. Full connection details are required in both sections, and it is strongly recommended + # to use two separate database IDs. + REDIS = { + 'tasks': { + 'URL': 'unix://${config.services.redis.servers.netbox.unixSocket}?db=0', + 'SSL': False, + }, + 'caching': { + 'URL': 'unix://${config.services.redis.servers.netbox.unixSocket}?db=1', + 'SSL': False, + } + } + + with open("${cfg.secretKeyFile}", "r") as file: + SECRET_KEY = file.readline() + + ${optionalString cfg.enableLdap "REMOTE_AUTH_BACKEND = 'netbox.authentication.LDAPBackend'"} + + ${cfg.extraConfig} + ''; + }; + pkg = (pkgs.netbox.overrideAttrs (old: { + installPhase = old.installPhase + '' + ln -s ${configFile} $out/opt/netbox/netbox/netbox/configuration.py + '' + optionalString cfg.enableLdap '' + ln -s ${ldapConfigPath} $out/opt/netbox/netbox/netbox/ldap_config.py + ''; + })).override { + plugins = ps: ((cfg.plugins ps) + ++ optional cfg.enableLdap [ ps.django-auth-ldap ]); + }; + netboxManageScript = with pkgs; (writeScriptBin "netbox-manage" '' + #!${stdenv.shell} + export PYTHONPATH=${pkg.pythonPath} + sudo -u netbox ${pkg}/bin/netbox "$@" + ''); + +in { + options.services.netbox = { + enable = mkOption { + type = lib.types.bool; + default = false; + description = lib.mdDoc '' + Enable Netbox. + + This module requires a reverse proxy that serves `/static` separately. + See this [example](https://github.com/netbox-community/netbox/blob/develop/contrib/nginx.conf/) on how to configure this. + ''; + }; + + listenAddress = mkOption { + type = types.str; + default = "[::1]"; + description = lib.mdDoc '' + Address the server will listen on. + ''; + }; + + port = mkOption { + type = types.port; + default = 8001; + description = lib.mdDoc '' + Port the server will listen on. + ''; + }; + + plugins = mkOption { + type = types.functionTo (types.listOf types.package); + default = _: []; + defaultText = literalExpression '' + python3Packages: with python3Packages; []; + ''; + description = lib.mdDoc '' + List of plugin packages to install. + ''; + }; + + dataDir = mkOption { + type = types.str; + default = "/var/lib/netbox"; + description = lib.mdDoc '' + Storage path of netbox. + ''; + }; + + secretKeyFile = mkOption { + type = types.path; + description = lib.mdDoc '' + Path to a file containing the secret key. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = lib.mdDoc '' + Additional lines of configuration appended to the `configuration.py`. + See the [documentation](https://netbox.readthedocs.io/en/stable/configuration/optional-settings/) for more possible options. + ''; + }; + + enableLdap = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Enable LDAP-Authentication for Netbox. + + This requires a configuration file being pass through `ldapConfigPath`. + ''; + }; + + ldapConfigPath = mkOption { + type = types.path; + default = ""; + description = lib.mdDoc '' + Path to the Configuration-File for LDAP-Authentification, will be loaded as `ldap_config.py`. + See the [documentation](https://netbox.readthedocs.io/en/stable/installation/6-ldap/#configuration) for possible options. + ''; + }; + }; + + config = mkIf cfg.enable { + services.redis.servers.netbox.enable = true; + + services.postgresql = { + enable = true; + ensureDatabases = [ "netbox" ]; + ensureUsers = [ + { + name = "netbox"; + ensurePermissions = { + "DATABASE netbox" = "ALL PRIVILEGES"; + }; + } + ]; + }; + + environment.systemPackages = [ netboxManageScript ]; + + systemd.targets.netbox = { + description = "Target for all NetBox services"; + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" "redis-netbox.service" ]; + }; + + systemd.services = let + defaultServiceConfig = { + WorkingDirectory = "${cfg.dataDir}"; + User = "netbox"; + Group = "netbox"; + StateDirectory = "netbox"; + StateDirectoryMode = "0750"; + Restart = "on-failure"; + }; + in { + netbox-migration = { + description = "NetBox migrations"; + wantedBy = [ "netbox.target" ]; + + environment = { + PYTHONPATH = pkg.pythonPath; + }; + + serviceConfig = defaultServiceConfig // { + Type = "oneshot"; + ExecStart = '' + ${pkg}/bin/netbox migrate + ''; + }; + }; + + netbox = { + description = "NetBox WSGI Service"; + wantedBy = [ "netbox.target" ]; + after = [ "netbox-migration.service" ]; + + preStart = '' + ${pkg}/bin/netbox trace_paths --no-input + ${pkg}/bin/netbox collectstatic --no-input + ${pkg}/bin/netbox remove_stale_contenttypes --no-input + ''; + + environment = { + PYTHONPATH = pkg.pythonPath; + }; + + serviceConfig = defaultServiceConfig // { + ExecStart = '' + ${pkgs.python3Packages.gunicorn}/bin/gunicorn netbox.wsgi \ + --bind ${cfg.listenAddress}:${toString cfg.port} \ + --pythonpath ${pkg}/opt/netbox/netbox + ''; + }; + }; + + netbox-rq = { + description = "NetBox Request Queue Worker"; + wantedBy = [ "netbox.target" ]; + after = [ "netbox.service" ]; + + environment = { + PYTHONPATH = pkg.pythonPath; + }; + + serviceConfig = defaultServiceConfig // { + ExecStart = '' + ${pkg}/bin/netbox rqworker high default low + ''; + }; + }; + + netbox-housekeeping = { + description = "NetBox housekeeping job"; + after = [ "netbox.service" ]; + + environment = { + PYTHONPATH = pkg.pythonPath; + }; + + serviceConfig = defaultServiceConfig // { + Type = "oneshot"; + ExecStart = '' + ${pkg}/bin/netbox housekeeping + ''; + }; + }; + }; + + systemd.timers.netbox-housekeeping = { + description = "Run NetBox housekeeping job"; + wantedBy = [ "timers.target" ]; + + timerConfig = { + OnCalendar = "daily"; + }; + }; + + users.users.netbox = { + home = "${cfg.dataDir}"; + isSystemUser = true; + group = "netbox"; + }; + users.groups.netbox = {}; + users.groups."${config.services.redis.servers.netbox.user}".members = [ "netbox" ]; + }; +} diff --git a/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix b/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix index b32220a5e579..feee7494a71a 100644 --- a/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix +++ b/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix @@ -6,6 +6,8 @@ let cfg = config.services.nextcloud; fpm = config.services.phpfpm.pools.nextcloud; + jsonFormat = pkgs.formats.json {}; + inherit (cfg) datadir; phpPackage = cfg.phpPackage.buildEnv { @@ -80,18 +82,19 @@ in { enable = mkEnableOption "nextcloud"; hostName = mkOption { type = types.str; - description = "FQDN for the nextcloud instance."; + description = lib.mdDoc "FQDN for the nextcloud instance."; }; home = mkOption { type = types.str; default = "/var/lib/nextcloud"; - description = "Storage path of nextcloud."; + description = lib.mdDoc "Storage path of nextcloud."; }; datadir = mkOption { type = types.str; - defaultText = "config.services.nextcloud.home"; - description = '' - Data storage path of nextcloud. Will be <xref linkend="opt-services.nextcloud.home" /> by default. + default = config.services.nextcloud.home; + defaultText = literalExpression "config.services.nextcloud.home"; + description = lib.mdDoc '' + Data storage path of nextcloud. Will be [](#opt-services.nextcloud.home) by default. This folder will be populated with a config.php and data folder which contains the state of the instance (excl the database)."; ''; example = "/mnt/nextcloud-file"; @@ -99,10 +102,10 @@ in { extraApps = mkOption { type = types.attrsOf types.package; default = { }; - description = '' + description = lib.mdDoc '' Extra apps to install. Should be an attrSet of appid to packages generated by fetchNextcloudApp. The appid must be identical to the "id" value in the apps appinfo/info.xml. - Using this will disable the appstore to prevent Nextcloud from updating these apps (see <xref linkend="opt-services.nextcloud.appstoreEnable" />). + Using this will disable the appstore to prevent Nextcloud from updating these apps (see [](#opt-services.nextcloud.appstoreEnable)). ''; example = literalExpression '' { @@ -124,8 +127,8 @@ in { extraAppsEnable = mkOption { type = types.bool; default = true; - description = '' - Automatically enable the apps in <xref linkend="opt-services.nextcloud.extraApps" /> every time nextcloud starts. + description = lib.mdDoc '' + Automatically enable the apps in [](#opt-services.nextcloud.extraApps) every time nextcloud starts. If set to false, apps need to be enabled in the Nextcloud user interface or with nextcloud-occ app:enable. ''; }; @@ -133,33 +136,33 @@ in { type = types.nullOr types.bool; default = null; example = true; - description = '' + description = lib.mdDoc '' Allow the installation of apps and app updates from the store. - Enabled by default unless there are packages in <xref linkend="opt-services.nextcloud.extraApps" />. - Set to true to force enable the store even if <xref linkend="opt-services.nextcloud.extraApps" /> is used. + Enabled by default unless there are packages in [](#opt-services.nextcloud.extraApps). + Set to true to force enable the store even if [](#opt-services.nextcloud.extraApps) is used. Set to false to disable the installation of apps from the global appstore. App management is always enabled regardless of this setting. ''; }; logLevel = mkOption { type = types.ints.between 0 4; default = 2; - description = "Log level value between 0 (DEBUG) and 4 (FATAL)."; + description = lib.mdDoc "Log level value between 0 (DEBUG) and 4 (FATAL)."; }; https = mkOption { type = types.bool; default = false; - description = "Use https for generated links."; + description = lib.mdDoc "Use https for generated links."; }; package = mkOption { type = types.package; - description = "Which package to use for the Nextcloud instance."; - relatedPackages = [ "nextcloud22" "nextcloud23" ]; + description = lib.mdDoc "Which package to use for the Nextcloud instance."; + relatedPackages = [ "nextcloud23" "nextcloud24" ]; }; phpPackage = mkOption { type = types.package; - relatedPackages = [ "php74" "php80" ]; + relatedPackages = [ "php80" "php81" ]; defaultText = "pkgs.php"; - description = '' + description = lib.mdDoc '' PHP package to use for Nextcloud. ''; }; @@ -167,7 +170,7 @@ in { maxUploadSize = mkOption { default = "512M"; type = types.str; - description = '' + description = lib.mdDoc '' Defines the upload limit for files. This changes the relevant options in php.ini and nginx if enabled. ''; @@ -176,7 +179,7 @@ in { skeletonDirectory = mkOption { default = ""; type = types.str; - description = '' + description = lib.mdDoc '' The directory where the skeleton files are located. These files will be copied to the data directory of new users. Leave empty to not copy any skeleton files. @@ -186,7 +189,7 @@ in { webfinger = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Enable this option if you plan on using the webfinger plugin. The appropriate nginx rewrite rules will be added to your configuration. ''; @@ -196,7 +199,7 @@ in { type = with types; functionTo (listOf package); default = all: []; defaultText = literalExpression "all: []"; - description = '' + description = lib.mdDoc '' Additional PHP extensions to use for nextcloud. By default, only extensions necessary for a vanilla nextcloud installation are enabled, but you may choose from the list of available extensions and add further ones. @@ -223,7 +226,7 @@ in { "openssl.cafile" = "/etc/ssl/certs/ca-certificates.crt"; catch_workers_output = "yes"; }; - description = '' + description = lib.mdDoc '' Options for PHP's php.ini file for nextcloud. ''; }; @@ -238,89 +241,106 @@ in { "pm.max_spare_servers" = "4"; "pm.max_requests" = "500"; }; - description = '' - Options for nextcloud's PHP pool. See the documentation on <literal>php-fpm.conf</literal> for details on configuration directives. + description = lib.mdDoc '' + Options for nextcloud's PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives. ''; }; poolConfig = mkOption { type = types.nullOr types.lines; default = null; - description = '' - Options for nextcloud's PHP pool. See the documentation on <literal>php-fpm.conf</literal> for details on configuration directives. + description = lib.mdDoc '' + Options for nextcloud's PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives. ''; }; + database = { + + createLocally = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Create the database and database user locally. Only available for + mysql database. + Note that this option will use the latest version of MariaDB which + is not officially supported by Nextcloud. As for now a workaround + is used to also support MariaDB version >= 10.6. + ''; + }; + + }; + + config = { dbtype = mkOption { type = types.enum [ "sqlite" "pgsql" "mysql" ]; default = "sqlite"; - description = "Database type."; + description = lib.mdDoc "Database type."; }; dbname = mkOption { type = types.nullOr types.str; default = "nextcloud"; - description = "Database name."; + description = lib.mdDoc "Database name."; }; dbuser = mkOption { type = types.nullOr types.str; default = "nextcloud"; - description = "Database user."; + description = lib.mdDoc "Database user."; }; dbpassFile = mkOption { type = types.nullOr types.str; default = null; - description = '' + description = lib.mdDoc '' The full path to a file that contains the database password. ''; }; dbhost = mkOption { type = types.nullOr types.str; default = "localhost"; - description = '' + description = lib.mdDoc '' Database host. Note: for using Unix authentication with PostgreSQL, this should be - set to <literal>/run/postgresql</literal>. + set to `/run/postgresql`. ''; }; dbport = mkOption { type = with types; nullOr (either int str); default = null; - description = "Database port."; + description = lib.mdDoc "Database port."; }; dbtableprefix = mkOption { type = types.nullOr types.str; default = null; - description = "Table prefix in Nextcloud database."; + description = lib.mdDoc "Table prefix in Nextcloud database."; }; adminuser = mkOption { type = types.str; default = "root"; - description = "Admin username."; + description = lib.mdDoc "Admin username."; }; adminpassFile = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' The full path to a file that contains the admin's password. Must be - readable by user <literal>nextcloud</literal>. + readable by user `nextcloud`. ''; }; extraTrustedDomains = mkOption { type = types.listOf types.str; default = []; - description = '' + description = lib.mdDoc '' Trusted domains, from which the nextcloud installation will be acessible. You don't need to add - <literal>services.nextcloud.hostname</literal> here. + `services.nextcloud.hostname` here. ''; }; trustedProxies = mkOption { type = types.listOf types.str; default = []; - description = '' + description = lib.mdDoc '' Trusted proxies, to provide if the nextcloud installation is being proxied to secure against e.g. spoofing. ''; @@ -331,10 +351,10 @@ in { default = null; example = "https"; - description = '' + description = lib.mdDoc '' Force Nextcloud to always use HTTPS i.e. for link generation. Nextcloud uses the currently used protocol by default, but when behind a reverse-proxy, - it may use <literal>http</literal> for everything although Nextcloud + it may use `http` for everything although Nextcloud may be served via HTTPS. ''; }; @@ -371,50 +391,50 @@ in { bucket = mkOption { type = types.str; example = "nextcloud"; - description = '' + description = lib.mdDoc '' The name of the S3 bucket. ''; }; autocreate = mkOption { type = types.bool; - description = '' + description = lib.mdDoc '' Create the objectstore if it does not exist. ''; }; key = mkOption { type = types.str; example = "EJ39ITYZEUH5BGWDRUFY"; - description = '' + description = lib.mdDoc '' The access key for the S3 bucket. ''; }; secretFile = mkOption { type = types.str; example = "/var/nextcloud-objectstore-s3-secret"; - description = '' + description = lib.mdDoc '' The full path to a file that contains the access secret. Must be - readable by user <literal>nextcloud</literal>. + readable by user `nextcloud`. ''; }; hostname = mkOption { type = types.nullOr types.str; default = null; example = "example.com"; - description = '' + description = lib.mdDoc '' Required for some non-Amazon implementations. ''; }; port = mkOption { type = types.nullOr types.port; default = null; - description = '' + description = lib.mdDoc '' Required for some non-Amazon implementations. ''; }; useSsl = mkOption { type = types.bool; default = true; - description = '' + description = lib.mdDoc '' Use SSL for objectstore access. ''; }; @@ -422,20 +442,20 @@ in { type = types.nullOr types.str; default = null; example = "REGION"; - description = '' + description = lib.mdDoc '' Required for some non-Amazon implementations. ''; }; usePathStyle = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Required for some non-Amazon S3 implementations. Ordinarily, requests will be made with - <literal>http://bucket.hostname.domain/</literal>, but with path style + `http://bucket.hostname.domain/`, but with path style enabled requests are made with - <literal>http://hostname.domain/bucket</literal> instead. + `http://hostname.domain/bucket` instead. ''; }; }; @@ -447,7 +467,7 @@ in { This is used by the theming app and for generating previews of certain images (e.g. SVG and HEIF). You may want to disable it for increased security. In that case, previews will still be available for some images (e.g. JPEG and PNG). - See <link xlink:href="https://github.com/nextcloud/server/issues/13099" />. + See <link xlink:href="https://github.com/nextcloud/server/issues/13099"/>. '' // { default = true; }; @@ -456,14 +476,14 @@ in { apcu = mkOption { type = types.bool; default = true; - description = '' + description = lib.mdDoc '' Whether to load the APCu module into PHP. ''; }; redis = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Whether to load the Redis module into PHP. You still need to enable Redis in your config.php. See https://docs.nextcloud.com/server/14/admin_manual/configuration_server/caching_configuration.html @@ -472,7 +492,7 @@ in { memcached = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Whether to load the Memcached module into PHP. You still need to enable Memcached in your config.php. See https://docs.nextcloud.com/server/14/admin_manual/configuration_server/caching_configuration.html @@ -483,7 +503,7 @@ in { enable = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Run regular auto update of all apps installed from the nextcloud app store. ''; }; @@ -505,17 +525,80 @@ in { The nextcloud-occ program preconfigured to target this Nextcloud instance. ''; }; + globalProfiles = mkEnableOption "global profiles" // { + description = '' + Makes user-profiles globally available under <literal>nextcloud.tld/u/user.name</literal>. + Even though it's enabled by default in Nextcloud, it must be explicitly enabled + here because it has the side-effect that personal information is even accessible to + unauthenticated users by default. + + By default, the following properties are set to <quote>Show to everyone</quote> + if this flag is enabled: + <itemizedlist> + <listitem><para>About</para></listitem> + <listitem><para>Full name</para></listitem> + <listitem><para>Headline</para></listitem> + <listitem><para>Organisation</para></listitem> + <listitem><para>Profile picture</para></listitem> + <listitem><para>Role</para></listitem> + <listitem><para>Twitter</para></listitem> + <listitem><para>Website</para></listitem> + </itemizedlist> + + Only has an effect in Nextcloud 23 and later. + ''; + }; - nginx.recommendedHttpHeaders = mkOption { - type = types.bool; - default = true; - description = "Enable additional recommended HTTP response headers"; + extraOptions = mkOption { + type = jsonFormat.type; + default = {}; + description = lib.mdDoc '' + Extra options which should be appended to nextcloud's config.php file. + ''; + example = literalExpression '' { + redis = { + host = "/run/redis/redis.sock"; + port = 0; + dbindex = 0; + password = "secret"; + timeout = 1.5; + }; + } ''; + }; + + secretFile = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Secret options which will be appended to nextcloud's config.php file (written as JSON, in the same + form as the <xref linkend="opt-services.nextcloud.extraOptions"/> option), for example + <programlisting>{"redis":{"password":"secret"}}</programlisting>. + ''; + }; + + nginx = { + recommendedHttpHeaders = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc "Enable additional recommended HTTP response headers"; + }; + hstsMaxAge = mkOption { + type = types.ints.positive; + default = 15552000; + description = lib.mdDoc '' + Value for the `max-age` directive of the HTTP + `Strict-Transport-Security` header. + + See section 6.1.1 of IETF RFC 6797 for detailed information on this + directive and header. + ''; + }; }; }; config = mkIf cfg.enable (mkMerge [ { warnings = let - latest = 23; + latest = 24; upgradeWarning = major: nixos: '' A legacy Nextcloud install (from before NixOS ${nixos}) may be installed. @@ -551,6 +634,7 @@ in { ++ (optional (versionOlder cfg.package.version "21") (upgradeWarning 20 "21.05")) ++ (optional (versionOlder cfg.package.version "22") (upgradeWarning 21 "21.11")) ++ (optional (versionOlder cfg.package.version "23") (upgradeWarning 22 "22.05")) + ++ (optional (versionOlder cfg.package.version "24") (upgradeWarning 23 "22.05")) ++ (optional isUnsupportedMariadb '' You seem to be using MariaDB at an unsupported version (i.e. at least 10.6)! Please note that this isn't supported officially by Nextcloud. You can either @@ -571,20 +655,30 @@ in { nextcloud defined in an overlay, please set `services.nextcloud.package` to `pkgs.nextcloud`. '' - else if versionOlder stateVersion "21.11" then nextcloud21 else if versionOlder stateVersion "22.05" then nextcloud22 - else nextcloud23 + else nextcloud24 ); - services.nextcloud.datadir = mkOptionDefault config.services.nextcloud.home; - services.nextcloud.phpPackage = - if versionOlder cfg.package.version "21" then pkgs.php74 + if versionOlder cfg.package.version "24" then pkgs.php80 + # FIXME: Use PHP 8.1 with Nextcloud 24 and higher, once issues like this one are fixed: + # + # https://github.com/nextcloud/twofactor_totp/issues/1192 + # + # else if versionOlder cfg.package.version "24" then pkgs.php80 + # else pkgs.php81; else pkgs.php80; } + { assertions = [ + { assertion = cfg.database.createLocally -> cfg.config.dbtype == "mysql"; + message = ''services.nextcloud.config.dbtype must be set to mysql if services.nextcloud.database.createLocally is set to true.''; + } + ]; } + { systemd.timers.nextcloud-cron = { wantedBy = [ "timers.target" ]; + after = [ "nextcloud-setup.service" ]; timerConfig.OnBootSec = "5m"; timerConfig.OnUnitActiveSec = "5m"; timerConfig.Unit = "nextcloud-cron.service"; @@ -627,6 +721,8 @@ in { if x == null then "false" else boolToString x; + nextcloudGreaterOrEqualThan = req: versionAtLeast cfg.package.version req; + overrideConfig = pkgs.writeText "nextcloud-config.php" '' <?php ${optionalString requiresReadSecretFunction '' @@ -639,10 +735,20 @@ in { $file )); } - return trim(file_get_contents($file)); + }''} + function nix_decode_json_file($file, $error) { + if (!file_exists($file)) { + throw new \RuntimeException(sprintf($error, $file)); } - ''} + $decoded = json_decode(file_get_contents($file), true); + + if (json_last_error() !== JSON_ERROR_NONE) { + throw new \RuntimeException(sprintf("Cannot decode %s, because: %s", $file, json_last_error_msg())); + } + + return $decoded; + } $CONFIG = [ 'apps_paths' => [ ${optionalString (cfg.extraApps != { }) "[ 'path' => '${cfg.home}/nix-apps', 'url' => '/nix-apps', 'writable' => false ],"} @@ -654,20 +760,38 @@ in { 'skeletondirectory' => '${cfg.skeletonDirectory}', ${optionalString cfg.caching.apcu "'memcache.local' => '\\OC\\Memcache\\APCu',"} 'log_type' => 'syslog', - 'log_level' => '${builtins.toString cfg.logLevel}', + 'loglevel' => '${builtins.toString cfg.logLevel}', ${optionalString (c.overwriteProtocol != null) "'overwriteprotocol' => '${c.overwriteProtocol}',"} ${optionalString (c.dbname != null) "'dbname' => '${c.dbname}',"} ${optionalString (c.dbhost != null) "'dbhost' => '${c.dbhost}',"} ${optionalString (c.dbport != null) "'dbport' => '${toString c.dbport}',"} ${optionalString (c.dbuser != null) "'dbuser' => '${c.dbuser}',"} ${optionalString (c.dbtableprefix != null) "'dbtableprefix' => '${toString c.dbtableprefix}',"} - ${optionalString (c.dbpassFile != null) "'dbpassword' => nix_read_secret('${c.dbpassFile}'),"} + ${optionalString (c.dbpassFile != null) '' + 'dbpassword' => nix_read_secret( + "${c.dbpassFile}" + ), + '' + } 'dbtype' => '${c.dbtype}', 'trusted_domains' => ${writePhpArrary ([ cfg.hostName ] ++ c.extraTrustedDomains)}, 'trusted_proxies' => ${writePhpArrary (c.trustedProxies)}, ${optionalString (c.defaultPhoneRegion != null) "'default_phone_region' => '${c.defaultPhoneRegion}',"} + ${optionalString (nextcloudGreaterOrEqualThan "23") "'profile.enabled' => ${boolToString cfg.globalProfiles},"} ${objectstoreConfig} ]; + + $CONFIG = array_replace_recursive($CONFIG, nix_decode_json_file( + "${jsonFormat.generate "nextcloud-extraOptions.json" cfg.extraOptions}", + "impossible: this should never happen (decoding generated extraOptions file %s failed)" + )); + + ${optionalString (cfg.secretFile != null) '' + $CONFIG = array_replace_recursive($CONFIG, nix_decode_json_file( + "${cfg.secretFile}", + "Cannot start Nextcloud, secrets file %s set by NixOS doesn't exist!" + )); + ''} ''; occInstallCmd = let mkExport = { arg, value }: "export ${arg}=${value}"; @@ -762,7 +886,7 @@ in { ${occ}/bin/nextcloud-occ config:system:delete trusted_domains ${optionalString (cfg.extraAppsEnable && cfg.extraApps != { }) '' - # Try to enable apps (don't fail when one of them cannot be enabled , eg. due to incompatible version) + # Try to enable apps ${occ}/bin/nextcloud-occ app:enable ${concatStringsSep " " (attrNames cfg.extraApps)} ''} @@ -772,12 +896,14 @@ in { serviceConfig.User = "nextcloud"; }; nextcloud-cron = { + after = [ "nextcloud-setup.service" ]; environment.NEXTCLOUD_CONFIG_DIR = "${datadir}/config"; serviceConfig.Type = "oneshot"; serviceConfig.User = "nextcloud"; serviceConfig.ExecStart = "${phpPackage}/bin/php -f ${cfg.package}/cron.php"; }; nextcloud-update-plugins = mkIf cfg.autoUpdateApps.enable { + after = [ "nextcloud-setup.service" ]; serviceConfig.Type = "oneshot"; serviceConfig.ExecStart = "${occ}/bin/nextcloud-occ app:update --all"; serviceConfig.User = "nextcloud"; @@ -811,6 +937,32 @@ in { environment.systemPackages = [ occ ]; + services.mysql = lib.mkIf cfg.database.createLocally { + enable = true; + package = lib.mkDefault pkgs.mariadb; + ensureDatabases = [ cfg.config.dbname ]; + ensureUsers = [{ + name = cfg.config.dbuser; + ensurePermissions = { "${cfg.config.dbname}.*" = "ALL PRIVILEGES"; }; + }]; + # FIXME(@Ma27) Nextcloud isn't compatible with mariadb 10.6, + # this is a workaround. + # See https://help.nextcloud.com/t/update-to-next-cloud-21-0-2-has-get-an-error/117028/22 + settings = mkIf (versionOlder cfg.package.version "24") { + mysqld = { + innodb_read_only_compressed = 0; + }; + }; + initialScript = pkgs.writeText "mysql-init" '' + CREATE USER '${cfg.config.dbname}'@'localhost' IDENTIFIED BY '${builtins.readFile( cfg.config.dbpassFile )}'; + CREATE DATABASE IF NOT EXISTS ${cfg.config.dbname}; + GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, + CREATE TEMPORARY TABLES ON ${cfg.config.dbname}.* TO '${cfg.config.dbuser}'@'localhost' + IDENTIFIED BY '${builtins.readFile( cfg.config.dbpassFile )}'; + FLUSH privileges; + ''; + }; + services.nginx.enable = mkDefault true; services.nginx.virtualHosts.${cfg.hostName} = { @@ -820,7 +972,6 @@ in { priority = 100; extraConfig = '' allow all; - log_not_found off; access_log off; ''; }; @@ -908,7 +1059,9 @@ in { add_header X-Permitted-Cross-Domain-Policies none; add_header X-Frame-Options sameorigin; add_header Referrer-Policy no-referrer; - add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always; + ''} + ${optionalString (cfg.https) '' + add_header Strict-Transport-Security "max-age=${toString cfg.nginx.hstsMaxAge}; includeSubDomains" always; ''} client_max_body_size ${cfg.maxUploadSize}; fastcgi_buffers 64 4K; diff --git a/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml b/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml index 8f55086a2bd1..b46f34420a70 100644 --- a/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml +++ b/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml @@ -11,7 +11,7 @@ desktop client is packaged at <literal>pkgs.nextcloud-client</literal>. </para> <para> - The current default by NixOS is <package>nextcloud23</package> which is also the latest + The current default by NixOS is <package>nextcloud24</package> which is also the latest major version available. </para> <section xml:id="module-services-nextcloud-basic-usage"> diff --git a/nixpkgs/nixos/modules/services/web-apps/nexus.nix b/nixpkgs/nixos/modules/services/web-apps/nexus.nix index dc50a06705f3..cfa137e77d25 100644 --- a/nixpkgs/nixos/modules/services/web-apps/nexus.nix +++ b/nixpkgs/nixos/modules/services/web-apps/nexus.nix @@ -17,37 +17,37 @@ in type = types.package; default = pkgs.nexus; defaultText = literalExpression "pkgs.nexus"; - description = "Package which runs Nexus3"; + description = lib.mdDoc "Package which runs Nexus3"; }; user = mkOption { type = types.str; default = "nexus"; - description = "User which runs Nexus3."; + description = lib.mdDoc "User which runs Nexus3."; }; group = mkOption { type = types.str; default = "nexus"; - description = "Group which runs Nexus3."; + description = lib.mdDoc "Group which runs Nexus3."; }; home = mkOption { type = types.str; default = "/var/lib/sonatype-work"; - description = "Home directory of the Nexus3 instance."; + description = lib.mdDoc "Home directory of the Nexus3 instance."; }; listenAddress = mkOption { type = types.str; default = "127.0.0.1"; - description = "Address to listen on."; + description = lib.mdDoc "Address to listen on."; }; listenPort = mkOption { type = types.int; default = 8081; - description = "Port to listen on."; + description = lib.mdDoc "Port to listen on."; }; jvmOpts = mkOption { diff --git a/nixpkgs/nixos/modules/services/web-apps/nifi.nix b/nixpkgs/nixos/modules/services/web-apps/nifi.nix new file mode 100644 index 000000000000..e3f30c710e0c --- /dev/null +++ b/nixpkgs/nixos/modules/services/web-apps/nifi.nix @@ -0,0 +1,318 @@ +{ lib, pkgs, config, options, ... }: + +let + cfg = config.services.nifi; + opt = options.services.nifi; + + env = { + NIFI_OVERRIDE_NIFIENV = "true"; + NIFI_HOME = "/var/lib/nifi"; + NIFI_PID_DIR = "/run/nifi"; + NIFI_LOG_DIR = "/var/log/nifi"; + }; + + envFile = pkgs.writeText "nifi.env" (lib.concatMapStrings (s: s + "\n") ( + (lib.concatLists (lib.mapAttrsToList (name: value: + if value != null then [ + "${name}=\"${toString value}\"" + ] else [] + ) env)))); + + nifiEnv = pkgs.writeShellScriptBin "nifi-env" '' + set -a + source "${envFile}" + eval -- "\$@" + ''; + +in { + options = { + services.nifi = { + enable = lib.mkEnableOption "Apache NiFi"; + + package = lib.mkOption { + type = lib.types.package; + default = pkgs.nifi; + defaultText = lib.literalExpression "pkgs.nifi"; + description = lib.mdDoc "Apache NiFi package to use."; + }; + + user = lib.mkOption { + type = lib.types.str; + default = "nifi"; + description = lib.mdDoc "User account where Apache NiFi runs."; + }; + + group = lib.mkOption { + type = lib.types.str; + default = "nifi"; + description = lib.mdDoc "Group account where Apache NiFi runs."; + }; + + enableHTTPS = lib.mkOption { + type = lib.types.bool; + default = true; + description = lib.mdDoc "Enable HTTPS protocol. Don`t use in production."; + }; + + listenHost = lib.mkOption { + type = lib.types.str; + default = if cfg.enableHTTPS then "0.0.0.0" else "127.0.0.1"; + defaultText = lib.literalExpression '' + if config.${opt.enableHTTPS} + then "0.0.0.0" + else "127.0.0.1" + ''; + description = lib.mdDoc "Bind to an ip for Apache NiFi web-ui."; + }; + + listenPort = lib.mkOption { + type = lib.types.int; + default = if cfg.enableHTTPS then 8443 else 8080; + defaultText = lib.literalExpression '' + if config.${opt.enableHTTPS} + then "8443" + else "8000" + ''; + description = lib.mdDoc "Bind to a port for Apache NiFi web-ui."; + }; + + proxyHost = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = if cfg.enableHTTPS then "0.0.0.0" else null; + defaultText = lib.literalExpression '' + if config.${opt.enableHTTPS} + then "0.0.0.0" + else null + ''; + description = lib.mdDoc "Allow requests from a specific host."; + }; + + proxyPort = lib.mkOption { + type = lib.types.nullOr lib.types.int; + default = if cfg.enableHTTPS then 8443 else null; + defaultText = lib.literalExpression '' + if config.${opt.enableHTTPS} + then "8443" + else null + ''; + description = lib.mdDoc "Allow requests from a specific port."; + }; + + initUser = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = lib.mdDoc "Initial user account for Apache NiFi. Username must be at least 4 characters."; + }; + + initPasswordFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + example = "/run/keys/nifi/password-nifi"; + description = lib.mdDoc "nitial password for Apache NiFi. Password must be at least 12 characters."; + }; + + initJavaHeapSize = lib.mkOption { + type = lib.types.nullOr lib.types.int; + default = null; + example = 1024; + description = lib.mdDoc "Set the initial heap size for the JVM in MB."; + }; + + maxJavaHeapSize = lib.mkOption { + type = lib.types.nullOr lib.types.int; + default = null; + example = 2048; + description = lib.mdDoc "Set the initial heap size for the JVM in MB."; + }; + }; + }; + + config = lib.mkIf cfg.enable { + assertions = [ + { assertion = cfg.initUser!=null || cfg.initPasswordFile==null; + message = '' + <option>services.nifi.initUser</option> needs to be set if <option>services.nifi.initPasswordFile</option> enabled. + ''; + } + { assertion = cfg.initUser==null || cfg.initPasswordFile!=null; + message = '' + <option>services.nifi.initPasswordFile</option> needs to be set if <option>services.nifi.initUser</option> enabled. + ''; + } + { assertion = cfg.proxyHost==null || cfg.proxyPort!=null; + message = '' + <option>services.nifi.proxyPort</option> needs to be set if <option>services.nifi.proxyHost</option> value specified. + ''; + } + { assertion = cfg.proxyHost!=null || cfg.proxyPort==null; + message = '' + <option>services.nifi.proxyHost</option> needs to be set if <option>services.nifi.proxyPort</option> value specified. + ''; + } + { assertion = cfg.initJavaHeapSize==null || cfg.maxJavaHeapSize!=null; + message = '' + <option>services.nifi.maxJavaHeapSize</option> needs to be set if <option>services.nifi.initJavaHeapSize</option> value specified. + ''; + } + { assertion = cfg.initJavaHeapSize!=null || cfg.maxJavaHeapSize==null; + message = '' + <option>services.nifi.initJavaHeapSize</option> needs to be set if <option>services.nifi.maxJavaHeapSize</option> value specified. + ''; + } + ]; + + warnings = lib.optional (cfg.enableHTTPS==false) '' + Please do not disable HTTPS mode in production. In this mode, access to the nifi is opened without authentication. + ''; + + systemd.tmpfiles.rules = [ + "d '/var/lib/nifi/conf' 0750 ${cfg.user} ${cfg.group}" + "L+ '/var/lib/nifi/lib' - - - - ${cfg.package}/lib" + ]; + + + systemd.services.nifi = { + description = "Apache NiFi"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + environment = env; + path = [ pkgs.gawk ]; + + serviceConfig = { + Type = "forking"; + PIDFile = "/run/nifi/nifi.pid"; + ExecStartPre = pkgs.writeScript "nifi-pre-start.sh" '' + #!/bin/sh + umask 077 + test -f '/var/lib/nifi/conf/authorizers.xml' || (cp '${cfg.package}/share/nifi/conf/authorizers.xml' '/var/lib/nifi/conf/' && chmod 0640 '/var/lib/nifi/conf/authorizers.xml') + test -f '/var/lib/nifi/conf/bootstrap.conf' || (cp '${cfg.package}/share/nifi/conf/bootstrap.conf' '/var/lib/nifi/conf/' && chmod 0640 '/var/lib/nifi/conf/bootstrap.conf') + test -f '/var/lib/nifi/conf/bootstrap-hashicorp-vault.conf' || (cp '${cfg.package}/share/nifi/conf/bootstrap-hashicorp-vault.conf' '/var/lib/nifi/conf/' && chmod 0640 '/var/lib/nifi/conf/bootstrap-hashicorp-vault.conf') + test -f '/var/lib/nifi/conf/bootstrap-notification-services.xml' || (cp '${cfg.package}/share/nifi/conf/bootstrap-notification-services.xml' '/var/lib/nifi/conf/' && chmod 0640 '/var/lib/nifi/conf/bootstrap-notification-services.xml') + test -f '/var/lib/nifi/conf/logback.xml' || (cp '${cfg.package}/share/nifi/conf/logback.xml' '/var/lib/nifi/conf/' && chmod 0640 '/var/lib/nifi/conf/logback.xml') + test -f '/var/lib/nifi/conf/login-identity-providers.xml' || (cp '${cfg.package}/share/nifi/conf/login-identity-providers.xml' '/var/lib/nifi/conf/' && chmod 0640 '/var/lib/nifi/conf/login-identity-providers.xml') + test -f '/var/lib/nifi/conf/nifi.properties' || (cp '${cfg.package}/share/nifi/conf/nifi.properties' '/var/lib/nifi/conf/' && chmod 0640 '/var/lib/nifi/conf/nifi.properties') + test -f '/var/lib/nifi/conf/stateless-logback.xml' || (cp '${cfg.package}/share/nifi/conf/stateless-logback.xml' '/var/lib/nifi/conf/' && chmod 0640 '/var/lib/nifi/conf/stateless-logback.xml') + test -f '/var/lib/nifi/conf/stateless.properties' || (cp '${cfg.package}/share/nifi/conf/stateless.properties' '/var/lib/nifi/conf/' && chmod 0640 '/var/lib/nifi/conf/stateless.properties') + test -f '/var/lib/nifi/conf/state-management.xml' || (cp '${cfg.package}/share/nifi/conf/state-management.xml' '/var/lib/nifi/conf/' && chmod 0640 '/var/lib/nifi/conf/state-management.xml') + test -f '/var/lib/nifi/conf/zookeeper.properties' || (cp '${cfg.package}/share/nifi/conf/zookeeper.properties' '/var/lib/nifi/conf/' && chmod 0640 '/var/lib/nifi/conf/zookeeper.properties') + test -d '/var/lib/nifi/docs/html' || (mkdir -p /var/lib/nifi/docs && cp -r '${cfg.package}/share/nifi/docs/html' '/var/lib/nifi/docs/html') + ${lib.optionalString ((cfg.initUser != null) && (cfg.initPasswordFile != null)) '' + awk -F'[<|>]' '/property name="Username"/ {if ($3!="") f=1} END{exit !f}' /var/lib/nifi/conf/login-identity-providers.xml || ${cfg.package}/bin/nifi.sh set-single-user-credentials ${cfg.initUser} $(cat ${cfg.initPasswordFile}) + ''} + ${lib.optionalString (cfg.enableHTTPS == false) '' + sed -i /var/lib/nifi/conf/nifi.properties \ + -e 's|nifi.remote.input.secure=.*|nifi.remote.input.secure=false|g' \ + -e 's|nifi.web.http.host=.*|nifi.web.http.host=${cfg.listenHost}|g' \ + -e 's|nifi.web.http.port=.*|nifi.web.http.port=${(toString cfg.listenPort)}|g' \ + -e 's|nifi.web.https.host=.*|nifi.web.https.host=|g' \ + -e 's|nifi.web.https.port=.*|nifi.web.https.port=|g' \ + -e 's|nifi.security.keystore=.*|nifi.security.keystore=|g' \ + -e 's|nifi.security.keystoreType=.*|nifi.security.keystoreType=|g' \ + -e 's|nifi.security.truststore=.*|nifi.security.truststore=|g' \ + -e 's|nifi.security.truststoreType=.*|nifi.security.truststoreType=|g' \ + -e '/nifi.security.keystorePasswd/s|^|#|' \ + -e '/nifi.security.keyPasswd/s|^|#|' \ + -e '/nifi.security.truststorePasswd/s|^|#|' + ''} + ${lib.optionalString (cfg.enableHTTPS == true) '' + sed -i /var/lib/nifi/conf/nifi.properties \ + -e 's|nifi.remote.input.secure=.*|nifi.remote.input.secure=true|g' \ + -e 's|nifi.web.http.host=.*|nifi.web.http.host=|g' \ + -e 's|nifi.web.http.port=.*|nifi.web.http.port=|g' \ + -e 's|nifi.web.https.host=.*|nifi.web.https.host=${cfg.listenHost}|g' \ + -e 's|nifi.web.https.port=.*|nifi.web.https.port=${(toString cfg.listenPort)}|g' \ + -e 's|nifi.security.keystore=.*|nifi.security.keystore=./conf/keystore.p12|g' \ + -e 's|nifi.security.keystoreType=.*|nifi.security.keystoreType=PKCS12|g' \ + -e 's|nifi.security.truststore=.*|nifi.security.truststore=./conf/truststore.p12|g' \ + -e 's|nifi.security.truststoreType=.*|nifi.security.truststoreType=PKCS12|g' \ + -e '/nifi.security.keystorePasswd/s|^#\+||' \ + -e '/nifi.security.keyPasswd/s|^#\+||' \ + -e '/nifi.security.truststorePasswd/s|^#\+||' + ''} + ${lib.optionalString ((cfg.enableHTTPS == true) && (cfg.proxyHost != null) && (cfg.proxyPort != null)) '' + sed -i /var/lib/nifi/conf/nifi.properties \ + -e 's|nifi.web.proxy.host=.*|nifi.web.proxy.host=${cfg.proxyHost}:${(toString cfg.proxyPort)}|g' + ''} + ${lib.optionalString ((cfg.enableHTTPS == false) || (cfg.proxyHost == null) && (cfg.proxyPort == null)) '' + sed -i /var/lib/nifi/conf/nifi.properties \ + -e 's|nifi.web.proxy.host=.*|nifi.web.proxy.host=|g' + ''} + ${lib.optionalString ((cfg.initJavaHeapSize != null) && (cfg.maxJavaHeapSize != null))'' + sed -i /var/lib/nifi/conf/bootstrap.conf \ + -e 's|java.arg.2=.*|java.arg.2=-Xms${(toString cfg.initJavaHeapSize)}m|g' \ + -e 's|java.arg.3=.*|java.arg.3=-Xmx${(toString cfg.maxJavaHeapSize)}m|g' + ''} + ${lib.optionalString ((cfg.initJavaHeapSize == null) && (cfg.maxJavaHeapSize == null))'' + sed -i /var/lib/nifi/conf/bootstrap.conf \ + -e 's|java.arg.2=.*|java.arg.2=-Xms512m|g' \ + -e 's|java.arg.3=.*|java.arg.3=-Xmx512m|g' + ''} + ''; + ExecStart = "${cfg.package}/bin/nifi.sh start"; + ExecStop = "${cfg.package}/bin/nifi.sh stop"; + # User and group + User = cfg.user; + Group = cfg.group; + # Runtime directory and mode + RuntimeDirectory = "nifi"; + RuntimeDirectoryMode = "0750"; + # State directory and mode + StateDirectory = "nifi"; + StateDirectoryMode = "0750"; + # Logs directory and mode + LogsDirectory = "nifi"; + LogsDirectoryMode = "0750"; + # Proc filesystem + ProcSubset = "pid"; + ProtectProc = "invisible"; + # Access write directories + ReadWritePaths = [ cfg.initPasswordFile ]; + UMask = "0027"; + # Capabilities + CapabilityBoundingSet = ""; + # Security + NoNewPrivileges = true; + # Sandboxing + ProtectSystem = "strict"; + ProtectHome = true; + PrivateTmp = true; + PrivateDevices = true; + PrivateIPC = true; + PrivateUsers = true; + ProtectHostname = true; + ProtectClock = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + RestrictAddressFamilies = [ "AF_INET AF_INET6" ]; + RestrictNamespaces = true; + LockPersonality = true; + MemoryDenyWriteExecute = false; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RemoveIPC = true; + PrivateMounts = true; + # System Call Filtering + SystemCallArchitectures = "native"; + SystemCallFilter = [ "~@cpu-emulation @debug @keyring @memlock @mount @obsolete @resources @privileged @setuid" "@chown" ]; + }; + }; + + users.users = lib.mkMerge [ + (lib.mkIf (cfg.user == "nifi") { + nifi = { + group = cfg.group; + isSystemUser = true; + home = cfg.package; + }; + }) + (lib.attrsets.setAttrByPath [ cfg.user "packages" ] [ cfg.package nifiEnv ]) + ]; + + users.groups = lib.optionalAttrs (cfg.group == "nifi") { + nifi = { }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/web-apps/node-red.nix b/nixpkgs/nixos/modules/services/web-apps/node-red.nix index 4512907f027b..e5b0998d3c41 100644 --- a/nixpkgs/nixos/modules/services/web-apps/node-red.nix +++ b/nixpkgs/nixos/modules/services/web-apps/node-red.nix @@ -23,13 +23,13 @@ in default = pkgs.nodePackages.node-red; defaultText = literalExpression "pkgs.nodePackages.node-red"; type = types.package; - description = "Node-RED package to use."; + description = lib.mdDoc "Node-RED package to use."; }; openFirewall = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Open ports in the firewall for the server. ''; }; @@ -37,7 +37,7 @@ in withNpmAndGcc = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Give Node-RED access to NPM and GCC at runtime, so 'Nodes' can be downloaded and managed imperatively via the 'Palette Manager'. ''; @@ -47,10 +47,9 @@ in type = types.path; default = "${cfg.package}/lib/node_modules/node-red/settings.js"; defaultText = literalExpression ''"''${package}/lib/node_modules/node-red/settings.js"''; - description = '' + description = lib.mdDoc '' Path to the JavaScript configuration file. - See <link - xlink:href="https://github.com/node-red/node-red/blob/master/packages/node_modules/node-red/settings.js"/> + See <https://github.com/node-red/node-red/blob/master/packages/node_modules/node-red/settings.js> for a configuration example. ''; }; @@ -58,13 +57,13 @@ in port = mkOption { type = types.port; default = 1880; - description = "Listening port."; + description = lib.mdDoc "Listening port."; }; user = mkOption { type = types.str; default = defaultUser; - description = '' + description = lib.mdDoc '' User under which Node-RED runs.If left as the default value this user will automatically be created on system activation, otherwise the sysadmin is responsible for ensuring the user exists. @@ -74,7 +73,7 @@ in group = mkOption { type = types.str; default = defaultUser; - description = '' + description = lib.mdDoc '' Group under which Node-RED runs.If left as the default value this group will automatically be created on system activation, otherwise the sysadmin is responsible for ensuring the group exists. @@ -84,7 +83,7 @@ in userDir = mkOption { type = types.path; default = "/var/lib/node-red"; - description = '' + description = lib.mdDoc '' The directory to store all user data, such as flow and credential files and all library data. If left as the default value this directory will automatically be created before the node-red service starts, otherwise the sysadmin is responsible for ensuring the directory exists with appropriate ownership @@ -95,13 +94,13 @@ in safe = mkOption { type = types.bool; default = false; - description = "Whether to launch Node-RED in --safe mode."; + description = lib.mdDoc "Whether to launch Node-RED in --safe mode."; }; define = mkOption { type = types.attrs; default = {}; - description = "List of settings.js overrides to pass via -D to Node-RED."; + description = lib.mdDoc "List of settings.js overrides to pass via -D to Node-RED."; example = literalExpression '' { "logging.console.level" = "trace"; diff --git a/nixpkgs/nixos/modules/services/web-apps/onlyoffice.nix b/nixpkgs/nixos/modules/services/web-apps/onlyoffice.nix new file mode 100644 index 000000000000..15fc3b03a834 --- /dev/null +++ b/nixpkgs/nixos/modules/services/web-apps/onlyoffice.nix @@ -0,0 +1,288 @@ +{ lib, config, pkgs, ... }: + +with lib; + +let + cfg = config.services.onlyoffice; +in +{ + options.services.onlyoffice = { + enable = mkEnableOption "OnlyOffice DocumentServer"; + + enableExampleServer = mkEnableOption "OnlyOffice example server"; + + hostname = mkOption { + type = types.str; + default = "localhost"; + description = lib.mdDoc "FQDN for the onlyoffice instance."; + }; + + jwtSecretFile = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc '' + Path to a file that contains the secret to sign web requests using JSON Web Tokens. + If left at the default value null signing is disabled. + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.onlyoffice-documentserver; + defaultText = "pkgs.onlyoffice-documentserver"; + description = lib.mdDoc "Which package to use for the OnlyOffice instance."; + }; + + port = mkOption { + type = types.port; + default = 8000; + description = lib.mdDoc "Port the OnlyOffice DocumentServer should listens on."; + }; + + examplePort = mkOption { + type = types.port; + default = null; + description = lib.mdDoc "Port the OnlyOffice Example server should listens on."; + }; + + postgresHost = mkOption { + type = types.str; + default = "/run/postgresql"; + description = lib.mdDoc "The Postgresql hostname or socket path OnlyOffice should connect to."; + }; + + postgresName = mkOption { + type = types.str; + default = "onlyoffice"; + description = lib.mdDoc "The name of databse OnlyOffice should user."; + }; + + postgresPasswordFile = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc '' + Path to a file that contains the password OnlyOffice should use to connect to Postgresql. + Unused when using socket authentication. + ''; + }; + + postgresUser = mkOption { + type = types.str; + default = "onlyoffice"; + description = lib.mdDoc '' + The username OnlyOffice should use to connect to Postgresql. + Unused when using socket authentication. + ''; + }; + + rabbitmqUrl = mkOption { + type = types.str; + default = "amqp://guest:guest@localhost:5672"; + description = lib.mdDoc "The Rabbitmq in amqp URI style OnlyOffice should connect to."; + }; + }; + + config = lib.mkIf cfg.enable { + services = { + nginx = { + enable = mkDefault true; + # misses text/csv, font/ttf, application/x-font-ttf, application/rtf, application/wasm + recommendedGzipSettings = mkDefault true; + recommendedProxySettings = mkDefault true; + + upstreams = { + # /etc/nginx/includes/http-common.conf + onlyoffice-docservice = { + servers = { "localhost:${toString cfg.port}" = { }; }; + }; + onlyoffice-example = lib.mkIf cfg.enableExampleServer { + servers = { "localhost:${toString cfg.examplePort}" = { }; }; + }; + }; + + virtualHosts.${cfg.hostname} = { + locations = { + # /etc/nginx/includes/ds-docservice.conf + "~ ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?\/(web-apps\/apps\/api\/documents\/api\.js)$".extraConfig = '' + expires -1; + alias ${cfg.package}/var/www/onlyoffice/documentserver/$2; + ''; + "~ ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?\/(web-apps)(\/.*\.json)$".extraConfig = '' + expires 365d; + error_log /dev/null crit; + alias ${cfg.package}/var/www/onlyoffice/documentserver/$2$3; + ''; + "~ ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?\/(sdkjs-plugins)(\/.*\.json)$".extraConfig = '' + expires 365d; + error_log /dev/null crit; + alias ${cfg.package}/var/www/onlyoffice/documentserver/$2$3; + ''; + "~ ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?\/(web-apps|sdkjs|sdkjs-plugins|fonts)(\/.*)$".extraConfig = '' + expires 365d; + alias ${cfg.package}/var/www/onlyoffice/documentserver/$2$3; + ''; + "~* ^(\/cache\/files.*)(\/.*)".extraConfig = '' + alias /var/lib/onlyoffice/documentserver/App_Data$1; + add_header Content-Disposition "attachment; filename*=UTF-8''$arg_filename"; + + set $secret_string verysecretstring; + secure_link $arg_md5,$arg_expires; + secure_link_md5 "$secure_link_expires$uri$secret_string"; + + if ($secure_link = "") { + return 403; + } + + if ($secure_link = "0") { + return 410; + } + ''; + "~* ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?\/(internal)(\/.*)$".extraConfig = '' + allow 127.0.0.1; + deny all; + proxy_pass http://onlyoffice-docservice/$2$3; + ''; + "~* ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?\/(info)(\/.*)$".extraConfig = '' + allow 127.0.0.1; + deny all; + proxy_pass http://onlyoffice-docservice/$2$3; + ''; + "/".extraConfig = '' + proxy_pass http://onlyoffice-docservice; + ''; + "~ ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?(\/doc\/.*)".extraConfig = '' + proxy_pass http://onlyoffice-docservice$2; + proxy_http_version 1.1; + ''; + "/${cfg.package.version}/".extraConfig = '' + proxy_pass http://onlyoffice-docservice/; + ''; + "~ ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?\/(dictionaries)(\/.*)$".extraConfig = '' + expires 365d; + alias ${cfg.package}/var/www/onlyoffice/documentserver/$2$3; + ''; + # /etc/nginx/includes/ds-example.conf + "~ ^(\/welcome\/.*)$".extraConfig = '' + expires 365d; + alias ${cfg.package}/var/www/onlyoffice/documentserver-example$1; + index docker.html; + ''; + "/example/".extraConfig = lib.mkIf cfg.enableExampleServer '' + proxy_pass http://onlyoffice-example/; + proxy_set_header X-Forwarded-Path /example; + ''; + }; + extraConfig = '' + rewrite ^/$ /welcome/ redirect; + rewrite ^\/OfficeWeb(\/apps\/.*)$ /${cfg.package.version}/web-apps$1 redirect; + rewrite ^(\/web-apps\/apps\/(?!api\/).*)$ /${cfg.package.version}$1 redirect; + + # based on https://github.com/ONLYOFFICE/document-server-package/blob/master/common/documentserver/nginx/includes/http-common.conf.m4#L29-L34 + # without variable indirection and correct variable names + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Proto $scheme; + # required for CSP to take effect + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # required for websocket + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + ''; + }; + }; + + rabbitmq.enable = lib.mkDefault true; + + postgresql = { + enable = lib.mkDefault true; + ensureDatabases = [ "onlyoffice" ]; + ensureUsers = [{ + name = "onlyoffice"; + ensurePermissions = { "DATABASE \"onlyoffice\"" = "ALL PRIVILEGES"; }; + }]; + }; + }; + + systemd.services = { + onlyoffice-converter = { + description = "onlyoffice converter"; + after = [ "network.target" "onlyoffice-docservice.service" "postgresql.service" ]; + requires = [ "network.target" "onlyoffice-docservice.service" "postgresql.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStart = "${cfg.package.fhs}/bin/onlyoffice-wrapper FileConverter/converter /run/onlyoffice/config"; + Group = "onlyoffice"; + Restart = "always"; + RuntimeDirectory = "onlyoffice"; + StateDirectory = "onlyoffice"; + Type = "simple"; + User = "onlyoffice"; + }; + }; + + onlyoffice-docservice = + let + onlyoffice-prestart = pkgs.writeShellScript "onlyoffice-prestart" '' + PATH=$PATH:${lib.makeBinPath (with pkgs; [ jq moreutils config.services.postgresql.package ])} + umask 077 + mkdir -p /run/onlyoffice/config/ /var/lib/onlyoffice/documentserver/sdkjs/{slide/themes,common}/ /var/lib/onlyoffice/documentserver/{fonts,server/FileConverter/bin}/ + cp -r ${cfg.package}/etc/onlyoffice/documentserver/* /run/onlyoffice/config/ + chmod u+w /run/onlyoffice/config/default.json + + cp /run/onlyoffice/config/default.json{,.orig} + + # for a mapping of environment variables from the docker container to json options see + # https://github.com/ONLYOFFICE/Docker-DocumentServer/blob/master/run-document-server.sh + jq ' + .services.CoAuthoring.server.port = ${toString cfg.port} | + .services.CoAuthoring.sql.dbHost = "${cfg.postgresHost}" | + .services.CoAuthoring.sql.dbName = "${cfg.postgresName}" | + ${lib.optionalString (cfg.postgresPasswordFile != null) '' + .services.CoAuthoring.sql.dbPass = "'"$(cat ${cfg.postgresPasswordFile})"'" | + ''} + .services.CoAuthoring.sql.dbUser = "${cfg.postgresUser}" | + ${lib.optionalString (cfg.jwtSecretFile != null) '' + .services.CoAuthoring.token.enable.browser = true | + .services.CoAuthoring.token.enable.request.inbox = true | + .services.CoAuthoring.token.enable.request.outbox = true | + .services.CoAuthoring.secret.inbox.string = "'"$(cat ${cfg.jwtSecretFile})"'" | + .services.CoAuthoring.secret.outbox.string = "'"$(cat ${cfg.jwtSecretFile})"'" | + .services.CoAuthoring.secret.session.string = "'"$(cat ${cfg.jwtSecretFile})"'" | + ''} + .rabbitmq.url = "${cfg.rabbitmqUrl}" + ' /run/onlyoffice/config/default.json | sponge /run/onlyoffice/config/default.json + + if ! psql -d onlyoffice -c "SELECT 'task_result'::regclass;" >/dev/null; then + psql -f ${cfg.package}/var/www/onlyoffice/documentserver/server/schema/postgresql/createdb.sql + fi + ''; + in + { + description = "onlyoffice documentserver"; + after = [ "network.target" "postgresql.service" ]; + requires = [ "postgresql.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStart = "${cfg.package.fhs}/bin/onlyoffice-wrapper DocService/docservice /run/onlyoffice/config"; + ExecStartPre = onlyoffice-prestart; + Group = "onlyoffice"; + Restart = "always"; + RuntimeDirectory = "onlyoffice"; + StateDirectory = "onlyoffice"; + Type = "simple"; + User = "onlyoffice"; + }; + }; + }; + + users.users = { + onlyoffice = { + description = "OnlyOffice Service"; + group = "onlyoffice"; + isSystemUser = true; + }; + }; + + users.groups.onlyoffice = { }; + }; +} diff --git a/nixpkgs/nixos/modules/services/web-apps/openwebrx.nix b/nixpkgs/nixos/modules/services/web-apps/openwebrx.nix index 9e90c01e0bbb..c409adbc7106 100644 --- a/nixpkgs/nixos/modules/services/web-apps/openwebrx.nix +++ b/nixpkgs/nixos/modules/services/web-apps/openwebrx.nix @@ -10,7 +10,7 @@ in type = types.package; default = pkgs.openwebrx; defaultText = literalExpression "pkgs.openwebrx"; - description = "OpenWebRX package to use for the service"; + description = lib.mdDoc "OpenWebRX package to use for the service"; }; }; @@ -19,6 +19,10 @@ in wantedBy = [ "multi-user.target" ]; path = with pkgs; [ csdr + digiham + codec2 + js8call + m17-cxx-demod alsaUtils netcat ]; diff --git a/nixpkgs/nixos/modules/services/web-apps/peertube.nix b/nixpkgs/nixos/modules/services/web-apps/peertube.nix index e195e6e6e824..c5a80e2d7d9d 100644 --- a/nixpkgs/nixos/modules/services/web-apps/peertube.nix +++ b/nixpkgs/nixos/modules/services/web-apps/peertube.nix @@ -11,6 +11,7 @@ let NODE_CONFIG_DIR = "/var/lib/peertube/config"; NODE_ENV = "production"; NODE_EXTRA_CA_CERTS = "/etc/ssl/certs/ca-certificates.crt"; + NPM_CONFIG_CACHE = "/var/cache/peertube/.npm"; NPM_CONFIG_PREFIX = cfg.package; HOME = cfg.package; }; @@ -73,51 +74,51 @@ in { user = lib.mkOption { type = lib.types.str; default = "peertube"; - description = "User account under which Peertube runs."; + description = lib.mdDoc "User account under which Peertube runs."; }; group = lib.mkOption { type = lib.types.str; default = "peertube"; - description = "Group under which Peertube runs."; + description = lib.mdDoc "Group under which Peertube runs."; }; localDomain = lib.mkOption { type = lib.types.str; example = "peertube.example.com"; - description = "The domain serving your PeerTube instance."; + description = lib.mdDoc "The domain serving your PeerTube instance."; }; listenHttp = lib.mkOption { type = lib.types.int; default = 9000; - description = "listen port for HTTP server."; + description = lib.mdDoc "listen port for HTTP server."; }; listenWeb = lib.mkOption { type = lib.types.int; default = 9000; - description = "listen port for WEB server."; + description = lib.mdDoc "listen port for WEB server."; }; enableWebHttps = lib.mkOption { type = lib.types.bool; default = false; - description = "Enable or disable HTTPS protocol."; + description = lib.mdDoc "Enable or disable HTTPS protocol."; }; dataDirs = lib.mkOption { type = lib.types.listOf lib.types.path; default = [ ]; example = [ "/opt/peertube/storage" "/var/cache/peertube" ]; - description = "Allow access to custom data locations."; + description = lib.mdDoc "Allow access to custom data locations."; }; serviceEnvironmentFile = lib.mkOption { type = lib.types.nullOr lib.types.path; default = null; example = "/run/keys/peertube/password-init-root"; - description = '' + description = lib.mdDoc '' Set environment variables for the service. Mainly useful for setting the initial root password. For example write to file: PT_INITIAL_ROOT_PASSWORD=changeme @@ -141,14 +142,14 @@ in { }; } ''; - description = "Configuration for peertube."; + description = lib.mdDoc "Configuration for peertube."; }; database = { createLocally = lib.mkOption { type = lib.types.bool; default = false; - description = "Configure local PostgreSQL database server for PeerTube."; + description = lib.mdDoc "Configure local PostgreSQL database server for PeerTube."; }; host = lib.mkOption { @@ -160,32 +161,32 @@ in { else null ''; example = "192.168.15.47"; - description = "Database host address or unix socket."; + description = lib.mdDoc "Database host address or unix socket."; }; port = lib.mkOption { type = lib.types.int; default = 5432; - description = "Database host port."; + description = lib.mdDoc "Database host port."; }; name = lib.mkOption { type = lib.types.str; default = "peertube"; - description = "Database name."; + description = lib.mdDoc "Database name."; }; user = lib.mkOption { type = lib.types.str; default = "peertube"; - description = "Database user."; + description = lib.mdDoc "Database user."; }; passwordFile = lib.mkOption { type = lib.types.nullOr lib.types.path; default = null; example = "/run/keys/peertube/password-posgressql-db"; - description = "Password for PostgreSQL database."; + description = lib.mdDoc "Password for PostgreSQL database."; }; }; @@ -193,7 +194,7 @@ in { createLocally = lib.mkOption { type = lib.types.bool; default = false; - description = "Configure local Redis server for PeerTube."; + description = lib.mdDoc "Configure local Redis server for PeerTube."; }; host = lib.mkOption { @@ -204,32 +205,32 @@ in { then "127.0.0.1" else null ''; - description = "Redis host."; + description = lib.mdDoc "Redis host."; }; port = lib.mkOption { type = lib.types.nullOr lib.types.port; - default = if cfg.redis.createLocally && cfg.redis.enableUnixSocket then null else 6379; + default = if cfg.redis.createLocally && cfg.redis.enableUnixSocket then null else 31638; defaultText = lib.literalExpression '' if config.${opt.redis.createLocally} && config.${opt.redis.enableUnixSocket} then null else 6379 ''; - description = "Redis port."; + description = lib.mdDoc "Redis port."; }; passwordFile = lib.mkOption { type = lib.types.nullOr lib.types.path; default = null; example = "/run/keys/peertube/password-redis-db"; - description = "Password for redis database."; + description = lib.mdDoc "Password for redis database."; }; enableUnixSocket = lib.mkOption { type = lib.types.bool; default = cfg.redis.createLocally; defaultText = lib.literalExpression "config.${opt.redis.createLocally}"; - description = "Use Unix socket."; + description = lib.mdDoc "Use Unix socket."; }; }; @@ -237,14 +238,14 @@ in { createLocally = lib.mkOption { type = lib.types.bool; default = false; - description = "Configure local Postfix SMTP server for PeerTube."; + description = lib.mdDoc "Configure local Postfix SMTP server for PeerTube."; }; passwordFile = lib.mkOption { type = lib.types.nullOr lib.types.path; default = null; example = "/run/keys/peertube/password-smtp"; - description = "Password for smtp server."; + description = lib.mdDoc "Password for smtp server."; }; }; @@ -252,7 +253,7 @@ in { type = lib.types.package; default = pkgs.peertube; defaultText = lib.literalExpression "pkgs.peertube"; - description = "Peertube package to use."; + description = lib.mdDoc "Peertube package to use."; }; }; @@ -344,7 +345,7 @@ in { }; }; } - (lib.mkIf cfg.redis.enableUnixSocket { redis = { socket = "/run/redis/redis.sock"; }; }) + (lib.mkIf cfg.redis.enableUnixSocket { redis = { socket = "/run/redis-peertube/redis.sock"; }; }) ]; systemd.tmpfiles.rules = [ @@ -425,6 +426,9 @@ in { # State directory and mode StateDirectory = "peertube"; StateDirectoryMode = "0750"; + # Cache directory and mode + CacheDirectory = "peertube"; + CacheDirectoryMode = "0750"; # Access write directories ReadWritePaths = cfg.dataDirs; # Environment @@ -441,13 +445,17 @@ in { enable = true; }; - services.redis = lib.mkMerge [ + services.redis.servers.peertube = lib.mkMerge [ (lib.mkIf cfg.redis.createLocally { enable = true; }) + (lib.mkIf (cfg.redis.createLocally && !cfg.redis.enableUnixSocket) { + bind = "127.0.0.1"; + port = cfg.redis.port; + }) (lib.mkIf (cfg.redis.createLocally && cfg.redis.enableUnixSocket) { - unixSocket = "/run/redis/redis.sock"; - unixSocketPerm = 770; + unixSocket = "/run/redis-peertube/redis.sock"; + unixSocketPerm = 660; }) ]; @@ -465,7 +473,7 @@ in { }; }) (lib.attrsets.setAttrByPath [ cfg.user "packages" ] [ cfg.package peertubeEnv peertubeCli pkgs.ffmpeg pkgs.nodejs-16_x pkgs.yarn ]) - (lib.mkIf cfg.redis.enableUnixSocket {${config.services.peertube.user}.extraGroups = [ "redis" ];}) + (lib.mkIf cfg.redis.enableUnixSocket {${config.services.peertube.user}.extraGroups = [ "redis-peertube" ];}) ]; users.groups = lib.optionalAttrs (cfg.group == "peertube") { diff --git a/nixpkgs/nixos/modules/services/web-apps/phylactery.nix b/nixpkgs/nixos/modules/services/web-apps/phylactery.nix new file mode 100644 index 000000000000..d512b48539b8 --- /dev/null +++ b/nixpkgs/nixos/modules/services/web-apps/phylactery.nix @@ -0,0 +1,51 @@ +{ config, lib, pkgs, ... }: + +with lib; +let cfg = config.services.phylactery; +in { + options.services.phylactery = { + enable = mkEnableOption "Whether to enable Phylactery server"; + + host = mkOption { + type = types.str; + default = "localhost"; + description = lib.mdDoc "Listen host for Phylactery"; + }; + + port = mkOption { + type = types.port; + description = lib.mdDoc "Listen port for Phylactery"; + }; + + library = mkOption { + type = types.path; + description = lib.mdDoc "Path to CBZ library"; + }; + + package = mkOption { + type = types.package; + default = pkgs.phylactery; + defaultText = literalExpression "pkgs.phylactery"; + description = lib.mdDoc "The Phylactery package to use"; + }; + }; + + config = mkIf cfg.enable { + systemd.services.phylactery = { + environment = { + PHYLACTERY_ADDRESS = "${cfg.host}:${toString cfg.port}"; + PHYLACTERY_LIBRARY = "${cfg.library}"; + }; + + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + ConditionPathExists = cfg.library; + DynamicUser = true; + ExecStart = "${cfg.package}/bin/phylactery"; + }; + }; + }; + + meta.maintainers = with maintainers; [ McSinyx ]; +} diff --git a/nixpkgs/nixos/modules/services/web-apps/pict-rs.nix b/nixpkgs/nixos/modules/services/web-apps/pict-rs.nix index e1847fbd5314..ab5a9ed07356 100644 --- a/nixpkgs/nixos/modules/services/web-apps/pict-rs.nix +++ b/nixpkgs/nixos/modules/services/web-apps/pict-rs.nix @@ -14,21 +14,21 @@ in dataDir = mkOption { type = types.path; default = "/var/lib/pict-rs"; - description = '' + description = lib.mdDoc '' The directory where to store the uploaded images. ''; }; address = mkOption { type = types.str; default = "127.0.0.1"; - description = '' + description = lib.mdDoc '' The IPv4 address to deploy the service to. ''; }; port = mkOption { type = types.port; default = 8080; - description = '' + description = lib.mdDoc '' The port which to bind the service to. ''; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/plantuml-server.nix b/nixpkgs/nixos/modules/services/web-apps/plantuml-server.nix index 9ea37b8a4cad..acd9292ceb45 100644 --- a/nixpkgs/nixos/modules/services/web-apps/plantuml-server.nix +++ b/nixpkgs/nixos/modules/services/web-apps/plantuml-server.nix @@ -17,7 +17,7 @@ in type = types.package; default = pkgs.plantuml-server; defaultText = literalExpression "pkgs.plantuml-server"; - description = "PlantUML server package to use"; + description = lib.mdDoc "PlantUML server package to use"; }; packages = { @@ -25,75 +25,75 @@ in type = types.package; default = pkgs.jdk; defaultText = literalExpression "pkgs.jdk"; - description = "JDK package to use for the server"; + description = lib.mdDoc "JDK package to use for the server"; }; jetty = mkOption { type = types.package; default = pkgs.jetty; defaultText = literalExpression "pkgs.jetty"; - description = "Jetty package to use for the server"; + description = lib.mdDoc "Jetty package to use for the server"; }; }; user = mkOption { type = types.str; default = "plantuml"; - description = "User which runs PlantUML server."; + description = lib.mdDoc "User which runs PlantUML server."; }; group = mkOption { type = types.str; default = "plantuml"; - description = "Group which runs PlantUML server."; + description = lib.mdDoc "Group which runs PlantUML server."; }; home = mkOption { type = types.str; default = "/var/lib/plantuml"; - description = "Home directory of the PlantUML server instance."; + description = lib.mdDoc "Home directory of the PlantUML server instance."; }; listenHost = mkOption { type = types.str; default = "127.0.0.1"; - description = "Host to listen on."; + description = lib.mdDoc "Host to listen on."; }; listenPort = mkOption { type = types.int; default = 8080; - description = "Port to listen on."; + description = lib.mdDoc "Port to listen on."; }; plantumlLimitSize = mkOption { type = types.int; default = 4096; - description = "Limits image width and height."; + description = lib.mdDoc "Limits image width and height."; }; graphvizPackage = mkOption { type = types.package; default = pkgs.graphviz; defaultText = literalExpression "pkgs.graphviz"; - description = "Package containing the dot executable."; + description = lib.mdDoc "Package containing the dot executable."; }; plantumlStats = mkOption { type = types.bool; default = false; - description = "Set it to on to enable statistics report (https://plantuml.com/statistics-report)."; + description = lib.mdDoc "Set it to on to enable statistics report (https://plantuml.com/statistics-report)."; }; httpAuthorization = mkOption { type = types.nullOr types.str; default = null; - description = "When calling the proxy endpoint, the value of HTTP_AUTHORIZATION will be used to set the HTTP Authorization header."; + description = lib.mdDoc "When calling the proxy endpoint, the value of HTTP_AUTHORIZATION will be used to set the HTTP Authorization header."; }; allowPlantumlInclude = mkOption { type = types.bool; default = false; - description = "Enables !include processing which can read files from the server into diagrams. Files are read relative to the current working directory."; + description = lib.mdDoc "Enables !include processing which can read files from the server into diagrams. Files are read relative to the current working directory."; }; }; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/plausible.nix b/nixpkgs/nixos/modules/services/web-apps/plausible.nix index 5d550ae5ca86..6f098134c922 100644 --- a/nixpkgs/nixos/modules/services/web-apps/plausible.nix +++ b/nixpkgs/nixos/modules/services/web-apps/plausible.nix @@ -11,7 +11,7 @@ in { releaseCookiePath = mkOption { type = with types; either str path; - description = '' + description = lib.mdDoc '' The path to the file with release cookie. (used for remote connection to the running node). ''; }; @@ -20,7 +20,7 @@ in { name = mkOption { default = "admin"; type = types.str; - description = '' + description = lib.mdDoc '' Name of the admin user that plausible will created on initial startup. ''; }; @@ -28,14 +28,14 @@ in { email = mkOption { type = types.str; example = "admin@localhost"; - description = '' + description = lib.mdDoc '' Email-address of the admin-user. ''; }; passwordFile = mkOption { type = types.either types.str types.path; - description = '' + description = lib.mdDoc '' Path to the file which contains the password of the admin user. ''; }; @@ -59,7 +59,7 @@ in { dbname = mkOption { default = "plausible"; type = types.str; - description = '' + description = lib.mdDoc '' Name of the database to use. ''; }; @@ -77,35 +77,35 @@ in { disableRegistration = mkOption { default = true; type = types.bool; - description = '' + description = lib.mdDoc '' Whether to prohibit creating an account in plausible's UI. ''; }; secretKeybaseFile = mkOption { type = types.either types.path types.str; - description = '' - Path to the secret used by the <literal>phoenix</literal>-framework. Instructions + description = lib.mdDoc '' + Path to the secret used by the `phoenix`-framework. Instructions how to generate one are documented in the - <link xlink:href="https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.Secret.html#content"> - framework docs</link>. + [ + framework docs](https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.Secret.html#content). ''; }; port = mkOption { default = 8000; type = types.port; - description = '' + description = lib.mdDoc '' Port where the service should be available. ''; }; baseUrl = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Public URL where plausible is available. - Note that <literal>/path</literal> components are currently ignored: - <link xlink:href="https://github.com/plausible/analytics/issues/1182"> + Note that `/path` components are currently ignored: + [ https://github.com/plausible/analytics/issues/1182 - </link>. + ](https://github.com/plausible/analytics/issues/1182). ''; }; }; @@ -114,8 +114,8 @@ in { email = mkOption { default = "hello@plausible.local"; type = types.str; - description = '' - The email id to use for as <emphasis>from</emphasis> address of all communications + description = lib.mdDoc '' + The email id to use for as *from* address of all communications from Plausible. ''; }; @@ -123,28 +123,28 @@ in { hostAddr = mkOption { default = "localhost"; type = types.str; - description = '' + description = lib.mdDoc '' The host address of your smtp server. ''; }; hostPort = mkOption { default = 25; type = types.port; - description = '' + description = lib.mdDoc '' The port of your smtp server. ''; }; user = mkOption { default = null; type = types.nullOr types.str; - description = '' + description = lib.mdDoc '' The username/email in case SMTP auth is enabled. ''; }; passwordFile = mkOption { default = null; type = with types; nullOr (either str path); - description = '' + description = lib.mdDoc '' The path to the file with the password in case SMTP auth is enabled. ''; }; @@ -152,7 +152,7 @@ in { retries = mkOption { type = types.ints.unsigned; default = 2; - description = '' + description = lib.mdDoc '' Number of retries to make until mailer gives up. ''; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/powerdns-admin.nix b/nixpkgs/nixos/modules/services/web-apps/powerdns-admin.nix index 4661ba80c5d6..c2d65f59e4dc 100644 --- a/nixpkgs/nixos/modules/services/web-apps/powerdns-admin.nix +++ b/nixpkgs/nixos/modules/services/web-apps/powerdns-admin.nix @@ -27,7 +27,7 @@ in example = literalExpression '' [ "-b" "127.0.0.1:8000" ] ''; - description = '' + description = lib.mdDoc '' Extra arguments passed to powerdns-admin. ''; }; @@ -40,9 +40,9 @@ in PORT = 8000 SQLALCHEMY_DATABASE_URI = 'postgresql://powerdnsadmin@/powerdnsadmin?host=/run/postgresql' ''; - description = '' + description = lib.mdDoc '' Configuration python file. - See <link xlink:href="https://github.com/ngoduykhanh/PowerDNS-Admin/blob/v${pkgs.powerdns-admin.version}/configs/development.py">the example configuration</link> + See [the example configuration](https://github.com/ngoduykhanh/PowerDNS-Admin/blob/v${pkgs.powerdns-admin.version}/configs/development.py) for options. ''; }; @@ -50,7 +50,7 @@ in secretKeyFile = mkOption { type = types.nullOr types.path; example = "/etc/powerdns-admin/secret"; - description = '' + description = lib.mdDoc '' The secret used to create cookies. This needs to be set, otherwise the default is used and everyone can forge valid login cookies. Set this to null to ignore this setting and configure it through another way. @@ -60,7 +60,7 @@ in saltFile = mkOption { type = types.nullOr types.path; example = "/etc/powerdns-admin/salt"; - description = '' + description = lib.mdDoc '' The salt used for serialization. This should be set, otherwise the default is used. Set this to null to ignore this setting and configure it through another way. diff --git a/nixpkgs/nixos/modules/services/web-apps/prosody-filer.nix b/nixpkgs/nixos/modules/services/web-apps/prosody-filer.nix index a901a95fd5f9..1d40809c420c 100644 --- a/nixpkgs/nixos/modules/services/web-apps/prosody-filer.nix +++ b/nixpkgs/nixos/modules/services/web-apps/prosody-filer.nix @@ -14,9 +14,9 @@ in { enable = mkEnableOption "Prosody Filer XMPP upload file server"; settings = mkOption { - description = '' + description = lib.mdDoc '' Configuration for Prosody Filer. - Refer to <link xlink:href="https://github.com/ThomasLeister/prosody-filer#configure-prosody-filer"/> for details on supported values. + Refer to <https://github.com/ThomasLeister/prosody-filer#configure-prosody-filer> for details on supported values. ''; type = settingsFormat.type; diff --git a/nixpkgs/nixos/modules/services/web-apps/restya-board.nix b/nixpkgs/nixos/modules/services/web-apps/restya-board.nix index 4b36cc8754c6..ae80a7866a16 100644 --- a/nixpkgs/nixos/modules/services/web-apps/restya-board.nix +++ b/nixpkgs/nixos/modules/services/web-apps/restya-board.nix @@ -30,7 +30,7 @@ in dataDir = mkOption { type = types.path; default = "/var/lib/restya-board"; - description = '' + description = lib.mdDoc '' Data of the application. ''; }; @@ -38,7 +38,7 @@ in user = mkOption { type = types.str; default = "restya-board"; - description = '' + description = lib.mdDoc '' User account under which the web-application runs. ''; }; @@ -46,7 +46,7 @@ in group = mkOption { type = types.str; default = "nginx"; - description = '' + description = lib.mdDoc '' Group account under which the web-application runs. ''; }; @@ -55,7 +55,7 @@ in serverName = mkOption { type = types.str; default = "restya.board"; - description = '' + description = lib.mdDoc '' Name of the nginx virtualhost to use. ''; }; @@ -63,7 +63,7 @@ in listenHost = mkOption { type = types.str; default = "localhost"; - description = '' + description = lib.mdDoc '' Listen address for the virtualhost to use. ''; }; @@ -71,7 +71,7 @@ in listenPort = mkOption { type = types.int; default = 3000; - description = '' + description = lib.mdDoc '' Listen port for the virtualhost to use. ''; }; @@ -81,7 +81,7 @@ in host = mkOption { type = types.nullOr types.str; default = null; - description = '' + description = lib.mdDoc '' Host of the database. Leave 'null' to use a local PostgreSQL database. A local PostgreSQL database is initialized automatically. ''; @@ -90,7 +90,7 @@ in port = mkOption { type = types.nullOr types.int; default = 5432; - description = '' + description = lib.mdDoc '' The database's port. ''; }; @@ -98,7 +98,7 @@ in name = mkOption { type = types.str; default = "restya_board"; - description = '' + description = lib.mdDoc '' Name of the database. The database must exist. ''; }; @@ -106,7 +106,7 @@ in user = mkOption { type = types.str; default = "restya_board"; - description = '' + description = lib.mdDoc '' The database user. The user must exist and have access to the specified database. ''; @@ -115,7 +115,7 @@ in passwordFile = mkOption { type = types.nullOr types.path; default = null; - description = '' + description = lib.mdDoc '' The database user's password. 'null' if no password is set. ''; }; @@ -126,7 +126,7 @@ in type = types.nullOr types.str; default = null; example = "localhost"; - description = '' + description = lib.mdDoc '' Hostname to send outgoing mail. Null to use the system MTA. ''; }; @@ -134,7 +134,7 @@ in port = mkOption { type = types.int; default = 25; - description = '' + description = lib.mdDoc '' Port used to connect to SMTP server. ''; }; @@ -142,7 +142,7 @@ in login = mkOption { type = types.str; default = ""; - description = '' + description = lib.mdDoc '' SMTP authentication login used when sending outgoing mail. ''; }; @@ -150,7 +150,7 @@ in password = mkOption { type = types.str; default = ""; - description = '' + description = lib.mdDoc '' SMTP authentication password used when sending outgoing mail. ATTENTION: The password is stored world-readable in the nix-store! @@ -161,7 +161,7 @@ in timezone = mkOption { type = types.lines; default = "GMT"; - description = '' + description = lib.mdDoc '' Timezone the web-app runs in. ''; }; @@ -263,8 +263,8 @@ in serviceConfig.RemainAfterExit = true; wantedBy = [ "multi-user.target" ]; - requires = [ "postgresql.service" ]; - after = [ "network.target" "postgresql.service" ]; + requires = if cfg.database.host == null then [] else [ "postgresql.service" ]; + after = [ "network.target" ] ++ (if cfg.database.host == null then [] else [ "postgresql.service" ]); script = '' rm -rf "${runDir}" @@ -282,7 +282,7 @@ in sed -i "s/^.*'R_DB_PASSWORD'.*$/define('R_DB_PASSWORD', 'restya');/g" "${runDir}/server/php/config.inc.php" '' else '' sed -i "s/^.*'R_DB_HOST'.*$/define('R_DB_HOST', '${cfg.database.host}');/g" "${runDir}/server/php/config.inc.php" - sed -i "s/^.*'R_DB_PASSWORD'.*$/define('R_DB_PASSWORD', ${if cfg.database.passwordFile == null then "''" else "'file_get_contents(${cfg.database.passwordFile})'"});/g" "${runDir}/server/php/config.inc.php + sed -i "s/^.*'R_DB_PASSWORD'.*$/define('R_DB_PASSWORD', ${if cfg.database.passwordFile == null then "''" else "'$(cat ${cfg.database.passwordFile})');/g"}" "${runDir}/server/php/config.inc.php" ''} sed -i "s/^.*'R_DB_PORT'.*$/define('R_DB_PORT', '${toString cfg.database.port}');/g" "${runDir}/server/php/config.inc.php" sed -i "s/^.*'R_DB_NAME'.*$/define('R_DB_NAME', '${cfg.database.name}');/g" "${runDir}/server/php/config.inc.php" @@ -294,7 +294,7 @@ in ln -sf "${cfg.dataDir}/client/img" "${runDir}/client/img" chmod g+w "${runDir}/tmp/cache" - chown -R "${cfg.user}"."${cfg.group}" "${runDir}" + chown -R "${cfg.user}":"${cfg.group}" "${runDir}" mkdir -m 0750 -p "${cfg.dataDir}" @@ -302,9 +302,9 @@ in mkdir -m 0750 -p "${cfg.dataDir}/client/img" cp -r "${pkgs.restya-board}/media/"* "${cfg.dataDir}/media" cp -r "${pkgs.restya-board}/client/img/"* "${cfg.dataDir}/client/img" - chown "${cfg.user}"."${cfg.group}" "${cfg.dataDir}" - chown -R "${cfg.user}"."${cfg.group}" "${cfg.dataDir}/media" - chown -R "${cfg.user}"."${cfg.group}" "${cfg.dataDir}/client/img" + chown "${cfg.user}":"${cfg.group}" "${cfg.dataDir}" + chown -R "${cfg.user}":"${cfg.group}" "${cfg.dataDir}/media" + chown -R "${cfg.user}":"${cfg.group}" "${cfg.dataDir}/client/img" ${optionalString (cfg.database.host == null) '' if ! [ -e "${cfg.dataDir}/.db-initialized" ]; then diff --git a/nixpkgs/nixos/modules/services/web-apps/rss-bridge.nix b/nixpkgs/nixos/modules/services/web-apps/rss-bridge.nix index f2b6d9559823..b1a3907d1964 100644 --- a/nixpkgs/nixos/modules/services/web-apps/rss-bridge.nix +++ b/nixpkgs/nixos/modules/services/web-apps/rss-bridge.nix @@ -16,7 +16,7 @@ in user = mkOption { type = types.str; default = "nginx"; - description = '' + description = lib.mdDoc '' User account under which both the service and the web-application run. ''; }; @@ -24,7 +24,7 @@ in group = mkOption { type = types.str; default = "nginx"; - description = '' + description = lib.mdDoc '' Group under which the web-application run. ''; }; @@ -32,7 +32,7 @@ in pool = mkOption { type = types.str; default = poolName; - description = '' + description = lib.mdDoc '' Name of existing phpfpm pool that is used to run web-application. If not specified a pool will be created automatically with default values. @@ -42,16 +42,16 @@ in dataDir = mkOption { type = types.str; default = "/var/lib/rss-bridge"; - description = '' + description = lib.mdDoc '' Location in which cache directory will be created. - You can put <literal>config.ini.php</literal> in here. + You can put `config.ini.php` in here. ''; }; virtualHost = mkOption { type = types.nullOr types.str; default = "rss-bridge"; - description = '' + description = lib.mdDoc '' Name of the nginx virtualhost to use and setup. If null, do not setup any virtualhost. ''; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/selfoss.nix b/nixpkgs/nixos/modules/services/web-apps/selfoss.nix index 899976ac696c..016e053c802b 100644 --- a/nixpkgs/nixos/modules/services/web-apps/selfoss.nix +++ b/nixpkgs/nixos/modules/services/web-apps/selfoss.nix @@ -35,7 +35,7 @@ in user = mkOption { type = types.str; default = "nginx"; - description = '' + description = lib.mdDoc '' User account under which both the service and the web-application run. ''; }; @@ -43,7 +43,7 @@ in pool = mkOption { type = types.str; default = "${poolName}"; - description = '' + description = lib.mdDoc '' Name of existing phpfpm pool that is used to run web-application. If not specified a pool will be created automatically with default values. @@ -54,7 +54,7 @@ in type = mkOption { type = types.enum ["pgsql" "mysql" "sqlite"]; default = "sqlite"; - description = '' + description = lib.mdDoc '' Database to store feeds. Supported are sqlite, pgsql and mysql. ''; }; @@ -62,7 +62,7 @@ in host = mkOption { type = types.str; default = "localhost"; - description = '' + description = lib.mdDoc '' Host of the database (has no effect if type is "sqlite"). ''; }; @@ -70,7 +70,7 @@ in name = mkOption { type = types.str; default = "tt_rss"; - description = '' + description = lib.mdDoc '' Name of the existing database (has no effect if type is "sqlite"). ''; }; @@ -78,7 +78,7 @@ in user = mkOption { type = types.str; default = "tt_rss"; - description = '' + description = lib.mdDoc '' The database user. The user must exist and has access to the specified database (has no effect if type is "sqlite"). ''; @@ -87,7 +87,7 @@ in password = mkOption { type = types.nullOr types.str; default = null; - description = '' + description = lib.mdDoc '' The database user's password (has no effect if type is "sqlite"). ''; }; @@ -95,7 +95,7 @@ in port = mkOption { type = types.nullOr types.int; default = null; - description = '' + description = lib.mdDoc '' The database's port. If not set, the default ports will be provided (5432 and 3306 for pgsql and mysql respectively) (has no effect if type is "sqlite"). @@ -105,7 +105,7 @@ in extraConfig = mkOption { type = types.lines; default = ""; - description = '' + description = lib.mdDoc '' Extra configuration added to config.ini ''; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/shiori.nix b/nixpkgs/nixos/modules/services/web-apps/shiori.nix index bb2fc684e83b..494f8587306f 100644 --- a/nixpkgs/nixos/modules/services/web-apps/shiori.nix +++ b/nixpkgs/nixos/modules/services/web-apps/shiori.nix @@ -12,13 +12,13 @@ in { type = types.package; default = pkgs.shiori; defaultText = literalExpression "pkgs.shiori"; - description = "The Shiori package to use."; + description = lib.mdDoc "The Shiori package to use."; }; address = mkOption { type = types.str; default = ""; - description = '' + description = lib.mdDoc '' The IP address on which Shiori will listen. If empty, listens on all interfaces. ''; @@ -27,7 +27,7 @@ in { port = mkOption { type = types.port; default = 8080; - description = "The port of the Shiori web application"; + description = lib.mdDoc "The port of the Shiori web application"; }; }; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/snipe-it.nix b/nixpkgs/nixos/modules/services/web-apps/snipe-it.nix new file mode 100644 index 000000000000..c0d29b048a33 --- /dev/null +++ b/nixpkgs/nixos/modules/services/web-apps/snipe-it.nix @@ -0,0 +1,494 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.snipe-it; + snipe-it = pkgs.snipe-it.override { + dataDir = cfg.dataDir; + }; + db = cfg.database; + mail = cfg.mail; + + user = cfg.user; + group = cfg.group; + + tlsEnabled = cfg.nginx.addSSL || cfg.nginx.forceSSL || cfg.nginx.onlySSL || cfg.nginx.enableACME; + + # shell script for local administration + artisan = pkgs.writeScriptBin "snipe-it" '' + #! ${pkgs.runtimeShell} + cd ${snipe-it} + sudo=exec + if [[ "$USER" != ${user} ]]; then + sudo='exec /run/wrappers/bin/sudo -u ${user}' + fi + $sudo ${pkgs.php}/bin/php artisan $* + ''; +in { + options.services.snipe-it = { + + enable = mkEnableOption "A free open source IT asset/license management system"; + + user = mkOption { + default = "snipeit"; + description = lib.mdDoc "User snipe-it runs as."; + type = types.str; + }; + + group = mkOption { + default = "snipeit"; + description = lib.mdDoc "Group snipe-it runs as."; + type = types.str; + }; + + appKeyFile = mkOption { + description = lib.mdDoc '' + A file containing the Laravel APP_KEY - a 32 character long, + base64 encoded key used for encryption where needed. Can be + generated with `head -c 32 /dev/urandom | base64`. + ''; + example = "/run/keys/snipe-it/appkey"; + type = types.path; + }; + + hostName = lib.mkOption { + type = lib.types.str; + default = if config.networking.domain != null then + config.networking.fqdn + else + config.networking.hostName; + defaultText = lib.literalExpression "config.networking.fqdn"; + example = "snipe-it.example.com"; + description = lib.mdDoc '' + The hostname to serve Snipe-IT on. + ''; + }; + + appURL = mkOption { + description = lib.mdDoc '' + The root URL that you want to host Snipe-IT on. All URLs in Snipe-IT will be generated using this value. + If you change this in the future you may need to run a command to update stored URLs in the database. + Command example: `snipe-it snipe-it:update-url https://old.example.com https://new.example.com` + ''; + default = "http${lib.optionalString tlsEnabled "s"}://${cfg.hostName}"; + defaultText = '' + http''${lib.optionalString tlsEnabled "s"}://''${cfg.hostName} + ''; + example = "https://example.com"; + type = types.str; + }; + + dataDir = mkOption { + description = lib.mdDoc "snipe-it data directory"; + default = "/var/lib/snipe-it"; + type = types.path; + }; + + database = { + host = mkOption { + type = types.str; + default = "localhost"; + description = lib.mdDoc "Database host address."; + }; + port = mkOption { + type = types.port; + default = 3306; + description = lib.mdDoc "Database host port."; + }; + name = mkOption { + type = types.str; + default = "snipeit"; + description = lib.mdDoc "Database name."; + }; + user = mkOption { + type = types.str; + default = user; + defaultText = literalExpression "user"; + description = lib.mdDoc "Database username."; + }; + passwordFile = mkOption { + type = with types; nullOr path; + default = null; + example = "/run/keys/snipe-it/dbpassword"; + description = lib.mdDoc '' + A file containing the password corresponding to + {option}`database.user`. + ''; + }; + createLocally = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Create the database and database user locally."; + }; + }; + + mail = { + driver = mkOption { + type = types.enum [ "smtp" "sendmail" ]; + default = "smtp"; + description = lib.mdDoc "Mail driver to use."; + }; + host = mkOption { + type = types.str; + default = "localhost"; + description = lib.mdDoc "Mail host address."; + }; + port = mkOption { + type = types.port; + default = 1025; + description = lib.mdDoc "Mail host port."; + }; + encryption = mkOption { + type = with types; nullOr (enum [ "tls" "ssl" ]); + default = null; + description = lib.mdDoc "SMTP encryption mechanism to use."; + }; + user = mkOption { + type = with types; nullOr str; + default = null; + example = "snipeit"; + description = lib.mdDoc "Mail username."; + }; + passwordFile = mkOption { + type = with types; nullOr path; + default = null; + example = "/run/keys/snipe-it/mailpassword"; + description = lib.mdDoc '' + A file containing the password corresponding to + {option}`mail.user`. + ''; + }; + backupNotificationAddress = mkOption { + type = types.str; + default = "backup@example.com"; + description = lib.mdDoc "Email Address to send Backup Notifications to."; + }; + from = { + name = mkOption { + type = types.str; + default = "Snipe-IT Asset Management"; + description = lib.mdDoc "Mail \"from\" name."; + }; + address = mkOption { + type = types.str; + default = "mail@example.com"; + description = lib.mdDoc "Mail \"from\" address."; + }; + }; + replyTo = { + name = mkOption { + type = types.str; + default = "Snipe-IT Asset Management"; + description = lib.mdDoc "Mail \"reply-to\" name."; + }; + address = mkOption { + type = types.str; + default = "mail@example.com"; + description = lib.mdDoc "Mail \"reply-to\" address."; + }; + }; + }; + + maxUploadSize = mkOption { + type = types.str; + default = "18M"; + example = "1G"; + description = lib.mdDoc "The maximum size for uploads (e.g. images)."; + }; + + poolConfig = mkOption { + type = with types; attrsOf (oneOf [ str int bool ]); + 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 = lib.mdDoc '' + Options for the snipe-it PHP pool. See the documentation on `php-fpm.conf` + for details on configuration directives. + ''; + }; + + nginx = mkOption { + type = types.submodule ( + recursiveUpdate + (import ../web-servers/nginx/vhost-options.nix { inherit config lib; }) {} + ); + default = {}; + example = literalExpression '' + { + serverAliases = [ + "snipe-it.''${config.networking.domain}" + ]; + # To enable encryption and let let's encrypt take care of certificate + forceSSL = true; + enableACME = true; + } + ''; + description = lib.mdDoc '' + With this option, you can customize the nginx virtualHost settings. + ''; + }; + + config = mkOption { + type = with types; + attrsOf + (nullOr + (either + (oneOf [ + bool + int + port + path + str + ]) + (submodule { + options = { + _secret = mkOption { + type = nullOr (oneOf [ str path ]); + description = '' + The path to a file containing the value the + option should be set to in the final + configuration file. + ''; + }; + }; + }))); + default = {}; + example = literalExpression '' + { + ALLOWED_IFRAME_HOSTS = "https://example.com"; + WKHTMLTOPDF = "''${pkgs.wkhtmltopdf}/bin/wkhtmltopdf"; + AUTH_METHOD = "oidc"; + OIDC_NAME = "MyLogin"; + OIDC_DISPLAY_NAME_CLAIMS = "name"; + OIDC_CLIENT_ID = "snipe-it"; + OIDC_CLIENT_SECRET = {_secret = "/run/keys/oidc_secret"}; + OIDC_ISSUER = "https://keycloak.example.com/auth/realms/My%20Realm"; + OIDC_ISSUER_DISCOVER = true; + } + ''; + description = lib.mdDoc '' + Snipe-IT configuration options to set in the + {file}`.env` file. + Refer to <https://snipe-it.readme.io/docs/configuration> + for details on supported values. + + Settings containing secret data should be set to an attribute + set containing the attribute `_secret` - a + string pointing to a file containing the value the option + should be set to. See the example to get a better picture of + this: in the resulting {file}`.env` file, the + `OIDC_CLIENT_SECRET` key will be set to the + contents of the {file}`/run/keys/oidc_secret` + file. + ''; + }; + }; + + config = mkIf cfg.enable { + + assertions = [ + { assertion = db.createLocally -> db.user == user; + message = "services.snipe-it.database.user must be set to ${user} if services.snipe-it.database.createLocally is set true."; + } + { assertion = db.createLocally -> db.passwordFile == null; + message = "services.snipe-it.database.passwordFile cannot be specified if services.snipe-it.database.createLocally is set to true."; + } + ]; + + environment.systemPackages = [ artisan ]; + + services.snipe-it.config = { + APP_ENV = "production"; + APP_KEY._secret = cfg.appKeyFile; + APP_URL = cfg.appURL; + DB_HOST = db.host; + DB_PORT = db.port; + DB_DATABASE = db.name; + DB_USERNAME = db.user; + DB_PASSWORD._secret = db.passwordFile; + MAIL_DRIVER = mail.driver; + MAIL_FROM_NAME = mail.from.name; + MAIL_FROM_ADDR = mail.from.address; + MAIL_REPLYTO_NAME = mail.from.name; + MAIL_REPLYTO_ADDR = mail.from.address; + MAIL_BACKUP_NOTIFICATION_ADDRESS = mail.backupNotificationAddress; + MAIL_HOST = mail.host; + MAIL_PORT = mail.port; + MAIL_USERNAME = mail.user; + MAIL_ENCRYPTION = mail.encryption; + MAIL_PASSWORD._secret = mail.passwordFile; + APP_SERVICES_CACHE = "/run/snipe-it/cache/services.php"; + APP_PACKAGES_CACHE = "/run/snipe-it/cache/packages.php"; + APP_CONFIG_CACHE = "/run/snipe-it/cache/config.php"; + APP_ROUTES_CACHE = "/run/snipe-it/cache/routes-v7.php"; + APP_EVENTS_CACHE = "/run/snipe-it/cache/events.php"; + SESSION_SECURE_COOKIE = tlsEnabled; + }; + + services.mysql = mkIf db.createLocally { + enable = true; + package = mkDefault pkgs.mariadb; + ensureDatabases = [ db.name ]; + ensureUsers = [ + { name = db.user; + ensurePermissions = { "${db.name}.*" = "ALL PRIVILEGES"; }; + } + ]; + }; + + services.phpfpm.pools.snipe-it = { + inherit user group; + phpPackage = pkgs.php81; + phpOptions = '' + post_max_size = ${cfg.maxUploadSize} + upload_max_filesize = ${cfg.maxUploadSize} + ''; + settings = { + "listen.mode" = "0660"; + "listen.owner" = user; + "listen.group" = group; + } // cfg.poolConfig; + }; + + services.nginx = { + enable = mkDefault true; + virtualHosts."${cfg.hostName}" = mkMerge [ cfg.nginx { + root = mkForce "${snipe-it}/public"; + extraConfig = optionalString (cfg.nginx.addSSL || cfg.nginx.forceSSL || cfg.nginx.onlySSL || cfg.nginx.enableACME) "fastcgi_param HTTPS on;"; + locations = { + "/" = { + index = "index.php"; + extraConfig = ''try_files $uri $uri/ /index.php?$query_string;''; + }; + "~ \.php$" = { + extraConfig = '' + try_files $uri $uri/ /index.php?$query_string; + include ${config.services.nginx.package}/conf/fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param REDIRECT_STATUS 200; + fastcgi_pass unix:${config.services.phpfpm.pools."snipe-it".socket}; + ${optionalString (cfg.nginx.addSSL || cfg.nginx.forceSSL || cfg.nginx.onlySSL || cfg.nginx.enableACME) "fastcgi_param HTTPS on;"} + ''; + }; + "~ \.(js|css|gif|png|ico|jpg|jpeg)$" = { + extraConfig = "expires 365d;"; + }; + }; + }]; + }; + + systemd.services.snipe-it-setup = { + description = "Preperation tasks for snipe-it"; + before = [ "phpfpm-snipe-it.service" ]; + after = optional db.createLocally "mysql.service"; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + User = user; + WorkingDirectory = snipe-it; + RuntimeDirectory = "snipe-it/cache"; + RuntimeDirectoryMode = 0700; + }; + path = [ pkgs.replace-secret ]; + script = + let + isSecret = v: isAttrs v && v ? _secret && (isString v._secret || builtins.isPath v._secret); + snipeITEnvVars = lib.generators.toKeyValue { + mkKeyValue = lib.flip lib.generators.mkKeyValueDefault "=" { + mkValueString = v: with builtins; + if isInt v then toString v + else if isString v then "\"${v}\"" + else if true == v then "true" + else if false == v then "false" + else if isSecret v then + if (isString v._secret) then + hashString "sha256" v._secret + else + hashString "sha256" (builtins.readFile v._secret) + else throw "unsupported type ${typeOf v}: ${(lib.generators.toPretty {}) v}"; + }; + }; + secretPaths = lib.mapAttrsToList (_: v: v._secret) (lib.filterAttrs (_: isSecret) cfg.config); + mkSecretReplacement = file: '' + replace-secret ${escapeShellArgs [ + ( + if (isString file) then + builtins.hashString "sha256" file + else + builtins.hashString "sha256" (builtins.readFile file) + ) + file + "${cfg.dataDir}/.env" + ]} + ''; + secretReplacements = lib.concatMapStrings mkSecretReplacement secretPaths; + filteredConfig = lib.converge (lib.filterAttrsRecursive (_: v: ! elem v [ {} null ])) cfg.config; + snipeITEnv = pkgs.writeText "snipeIT.env" (snipeITEnvVars filteredConfig); + in '' + # error handling + set -euo pipefail + + # set permissions + umask 077 + + # create .env file + install -T -m 0600 -o ${user} ${snipeITEnv} "${cfg.dataDir}/.env" + + # replace secrets + ${secretReplacements} + + # prepend `base64:` if it does not exist in APP_KEY + if ! grep 'APP_KEY=base64:' "${cfg.dataDir}/.env" >/dev/null; then + sed -i 's/APP_KEY=/APP_KEY=base64:/' "${cfg.dataDir}/.env" + fi + + # purge cache + rm "${cfg.dataDir}"/bootstrap/cache/*.php || true + + # migrate db + ${pkgs.php}/bin/php artisan migrate --force + ''; + }; + + systemd.tmpfiles.rules = [ + "d ${cfg.dataDir} 0710 ${user} ${group} - -" + "d ${cfg.dataDir}/bootstrap 0750 ${user} ${group} - -" + "d ${cfg.dataDir}/bootstrap/cache 0750 ${user} ${group} - -" + "d ${cfg.dataDir}/public 0750 ${user} ${group} - -" + "d ${cfg.dataDir}/public/uploads 0750 ${user} ${group} - -" + "d ${cfg.dataDir}/storage 0700 ${user} ${group} - -" + "d ${cfg.dataDir}/storage/app 0700 ${user} ${group} - -" + "d ${cfg.dataDir}/storage/fonts 0700 ${user} ${group} - -" + "d ${cfg.dataDir}/storage/framework 0700 ${user} ${group} - -" + "d ${cfg.dataDir}/storage/framework/cache 0700 ${user} ${group} - -" + "d ${cfg.dataDir}/storage/framework/sessions 0700 ${user} ${group} - -" + "d ${cfg.dataDir}/storage/framework/views 0700 ${user} ${group} - -" + "d ${cfg.dataDir}/storage/logs 0700 ${user} ${group} - -" + "d ${cfg.dataDir}/storage/uploads 0700 ${user} ${group} - -" + "d ${cfg.dataDir}/storage/private_uploads 0700 ${user} ${group} - -" + ]; + + users = { + users = mkIf (user == "snipeit") { + snipeit = { + inherit group; + isSystemUser = true; + }; + "${config.services.nginx.user}".extraGroups = [ group ]; + }; + groups = mkIf (group == "snipeit") { + snipeit = {}; + }; + }; + + }; + + meta.maintainers = with maintainers; [ yayayayaka ]; +} diff --git a/nixpkgs/nixos/modules/services/web-apps/sogo.nix b/nixpkgs/nixos/modules/services/web-apps/sogo.nix index 4610bb96cb5e..a134282de83a 100644 --- a/nixpkgs/nixos/modules/services/web-apps/sogo.nix +++ b/nixpkgs/nixos/modules/services/web-apps/sogo.nix @@ -21,31 +21,31 @@ in { enable = mkEnableOption "SOGo groupware"; vhostName = mkOption { - description = "Name of the nginx vhost"; + description = lib.mdDoc "Name of the nginx vhost"; type = str; default = "sogo"; }; timezone = mkOption { - description = "Timezone of your SOGo instance"; + description = lib.mdDoc "Timezone of your SOGo instance"; type = str; example = "America/Montreal"; }; language = mkOption { - description = "Language of SOGo"; + description = lib.mdDoc "Language of SOGo"; type = str; default = "English"; }; ealarmsCredFile = mkOption { - description = "Optional path to a credentials file for email alarms"; + description = lib.mdDoc "Optional path to a credentials file for email alarms"; type = nullOr str; default = null; }; configReplaces = mkOption { - description = '' + description = lib.mdDoc '' Replacement-filepath mapping for sogo.conf. Every key is replaced with the contents of the file specified as value. @@ -60,7 +60,7 @@ in { }; extraConfig = mkOption { - description = "Extra sogo.conf configuration lines"; + description = lib.mdDoc "Extra sogo.conf configuration lines"; type = lines; default = ""; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/timetagger.nix b/nixpkgs/nixos/modules/services/web-apps/timetagger.nix deleted file mode 100644 index 373f4fcd52f8..000000000000 --- a/nixpkgs/nixos/modules/services/web-apps/timetagger.nix +++ /dev/null @@ -1,80 +0,0 @@ -{ config, lib, pkgs, ... }: - -let - inherit (lib) mkEnableOption mkIf mkOption types literalExpression; - - cfg = config.services.timetagger; -in { - - options = { - services.timetagger = { - enable = mkOption { - type = types.bool; - default = false; - description = '' - Tag your time, get the insight - - <note><para> - This app does not do authentication. - You must setup authentication yourself or run it in an environment where - only allowed users have access. - </para></note> - ''; - }; - - bindAddr = mkOption { - description = "Address to bind to."; - type = types.str; - default = "127.0.0.1"; - }; - - port = mkOption { - description = "Port to bind to."; - type = types.port; - default = 8080; - }; - - package = mkOption { - description = '' - Use own package for starting timetagger web application. - - The ${literalExpression ''pkgs.timetagger''} package only provides a - "run.py" script for the actual package - ${literalExpression ''pkgs.python3Packages.timetagger''}. - - If you want to provide a "run.py" script for starting timetagger - yourself, you can do so with this option. - If you do so, the 'bindAddr' and 'port' options are ignored. - ''; - - default = pkgs.timetagger.override { addr = cfg.bindAddr; port = cfg.port; }; - defaultText = literalExpression '' - pkgs.timetagger.override { - addr = ${cfg.bindAddr}; - port = ${cfg.port}; - }; - ''; - type = types.package; - }; - }; - }; - - config = mkIf cfg.enable { - systemd.services.timetagger = { - description = "Timetagger service"; - wantedBy = [ "multi-user.target" ]; - - serviceConfig = { - User = "timetagger"; - Group = "timetagger"; - StateDirectory = "timetagger"; - - ExecStart = "${cfg.package}/bin/timetagger"; - - Restart = "on-failure"; - RestartSec = 1; - }; - }; - }; -} - diff --git a/nixpkgs/nixos/modules/services/web-apps/trilium.nix b/nixpkgs/nixos/modules/services/web-apps/trilium.nix index 35383c992fe8..bb1061cf278e 100644 --- a/nixpkgs/nixos/modules/services/web-apps/trilium.nix +++ b/nixpkgs/nixos/modules/services/web-apps/trilium.nix @@ -10,6 +10,7 @@ let # Disable automatically generating desktop icon noDesktopIcon=true noBackup=${lib.boolToString cfg.noBackup} + noAuthentication=${lib.boolToString cfg.noAuthentication} [Network] # host setting is relevant only for web deployments - set the host on which the server will listen @@ -28,7 +29,7 @@ in dataDir = mkOption { type = types.str; default = "/var/lib/trilium"; - description = '' + description = lib.mdDoc '' The directory storing the notes database and the configuration. ''; }; @@ -36,7 +37,7 @@ in instanceName = mkOption { type = types.str; default = "Trilium"; - description = '' + description = lib.mdDoc '' Instance name used to distinguish between different instances ''; }; @@ -44,15 +45,23 @@ in noBackup = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Disable periodic database backups. ''; }; + noAuthentication = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + If set to true, no password is required to access the web frontend. + ''; + }; + host = mkOption { type = types.str; default = "127.0.0.1"; - description = '' + description = lib.mdDoc '' The host address to bind to (defaults to localhost). ''; }; @@ -60,14 +69,14 @@ in port = mkOption { type = types.int; default = 8080; - description = '' + description = lib.mdDoc '' The port number to bind to. ''; }; nginx = mkOption { default = {}; - description = '' + description = lib.mdDoc '' Configuration for nginx reverse proxy. ''; @@ -76,14 +85,14 @@ in enable = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Configure the nginx reverse proxy settings. ''; }; hostName = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' The hostname use to setup the virtualhost configuration ''; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix b/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix index 9aa38ab25c9a..f105b0aa3f72 100644 --- a/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix +++ b/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix @@ -126,7 +126,7 @@ let root = mkOption { type = types.path; default = "/var/lib/tt-rss"; - description = '' + description = lib.mdDoc '' Root of the application. ''; }; @@ -134,7 +134,7 @@ let user = mkOption { type = types.str; default = "tt_rss"; - description = '' + description = lib.mdDoc '' User account under which both the update daemon and the web-application run. ''; }; @@ -142,7 +142,7 @@ let pool = mkOption { type = types.str; default = "${poolName}"; - description = '' + description = lib.mdDoc '' Name of existing phpfpm pool that is used to run web-application. If not specified a pool will be created automatically with default values. @@ -152,7 +152,7 @@ let virtualHost = mkOption { type = types.nullOr types.str; default = "tt-rss"; - description = '' + description = lib.mdDoc '' Name of the nginx virtualhost to use and setup. If null, do not setup any virtualhost. ''; }; @@ -161,7 +161,7 @@ let type = mkOption { type = types.enum ["pgsql" "mysql"]; default = "pgsql"; - description = '' + description = lib.mdDoc '' Database to store feeds. Supported are pgsql and mysql. ''; }; @@ -169,7 +169,7 @@ let host = mkOption { type = types.nullOr types.str; default = null; - description = '' + description = lib.mdDoc '' Host of the database. Leave null to use Unix domain socket. ''; }; @@ -177,7 +177,7 @@ let name = mkOption { type = types.str; default = "tt_rss"; - description = '' + description = lib.mdDoc '' Name of the existing database. ''; }; @@ -185,7 +185,7 @@ let user = mkOption { type = types.str; default = "tt_rss"; - description = '' + description = lib.mdDoc '' The database user. The user must exist and has access to the specified database. ''; @@ -194,7 +194,7 @@ let password = mkOption { type = types.nullOr types.str; default = null; - description = '' + description = lib.mdDoc '' The database user's password. ''; }; @@ -202,7 +202,7 @@ let passwordFile = mkOption { type = types.nullOr types.str; default = null; - description = '' + description = lib.mdDoc '' The database user's password. ''; }; @@ -210,7 +210,7 @@ let port = mkOption { type = types.nullOr types.int; default = null; - description = '' + description = lib.mdDoc '' The database's port. If not set, the default ports will be provided (5432 and 3306 for pgsql and mysql respectively). ''; @@ -219,7 +219,7 @@ let createLocally = mkOption { type = types.bool; default = true; - description = "Create the database and database user locally."; + description = lib.mdDoc "Create the database and database user locally."; }; }; @@ -227,7 +227,7 @@ let autoCreate = mkOption { type = types.bool; default = true; - description = '' + description = lib.mdDoc '' Allow authentication modules to auto-create users in tt-rss internal database when authenticated successfully. ''; @@ -236,7 +236,7 @@ let autoLogin = mkOption { type = types.bool; default = true; - description = '' + description = lib.mdDoc '' Automatically login user on remote or other kind of externally supplied authentication, otherwise redirect to login form as normal. If set to true, users won't be able to set application language @@ -249,7 +249,7 @@ let hub = mkOption { type = types.str; default = ""; - description = '' + description = lib.mdDoc '' URL to a PubSubHubbub-compatible hub server. If defined, "Published articles" generated feed would automatically become PUSH-enabled. ''; @@ -258,7 +258,7 @@ let enable = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Enable client PubSubHubbub support in tt-rss. When disabled, tt-rss won't try to subscribe to PUSH feed updates. ''; @@ -269,7 +269,7 @@ let server = mkOption { type = types.str; default = "localhost:9312"; - description = '' + description = lib.mdDoc '' Hostname:port combination for the Sphinx server. ''; }; @@ -277,7 +277,7 @@ let index = mkOption { type = types.listOf types.str; default = ["ttrss" "delta"]; - description = '' + description = lib.mdDoc '' Index names in Sphinx configuration. Example configuration files are available on tt-rss wiki. ''; @@ -288,7 +288,7 @@ let enable = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Allow users to register themselves. Please be aware that allowing random people to access your tt-rss installation is a security risk and potentially might lead to data loss or server exploit. Disabled @@ -299,7 +299,7 @@ let notifyAddress = mkOption { type = types.str; default = ""; - description = '' + description = lib.mdDoc '' Email address to send new user notifications to. ''; }; @@ -307,7 +307,7 @@ let maxUsers = mkOption { type = types.int; default = 0; - description = '' + description = lib.mdDoc '' Maximum amount of users which will be allowed to register on this system. 0 - no limit. ''; @@ -319,7 +319,7 @@ let type = types.str; default = ""; example = "localhost:25"; - description = '' + description = lib.mdDoc '' Hostname:port combination to send outgoing mail. Blank - use system MTA. ''; @@ -328,7 +328,7 @@ let login = mkOption { type = types.str; default = ""; - description = '' + description = lib.mdDoc '' SMTP authentication login used when sending outgoing mail. ''; }; @@ -336,7 +336,7 @@ let password = mkOption { type = types.str; default = ""; - description = '' + description = lib.mdDoc '' SMTP authentication password used when sending outgoing mail. ''; }; @@ -344,7 +344,7 @@ let security = mkOption { type = types.enum ["" "ssl" "tls"]; default = ""; - description = '' + description = lib.mdDoc '' Used to select a secure SMTP connection. Allowed values: ssl, tls, or empty. ''; @@ -353,7 +353,7 @@ let fromName = mkOption { type = types.str; default = "Tiny Tiny RSS"; - description = '' + description = lib.mdDoc '' Name for sending outgoing mail. This applies to password reset notifications, digest emails and any other mail. ''; @@ -362,7 +362,7 @@ let fromAddress = mkOption { type = types.str; default = ""; - description = '' + description = lib.mdDoc '' Address for sending outgoing mail. This applies to password reset notifications, digest emails and any other mail. ''; @@ -371,7 +371,7 @@ let digestSubject = mkOption { type = types.str; default = "[tt-rss] New headlines for last 24 hours"; - description = '' + description = lib.mdDoc '' Subject line for email digests. ''; }; @@ -380,7 +380,7 @@ let sessionCookieLifetime = mkOption { type = types.int; default = 86400; - description = '' + description = lib.mdDoc '' Default lifetime of a session (e.g. login) cookie. In seconds, 0 means cookie will be deleted when browser closes. ''; @@ -388,7 +388,7 @@ let selfUrlPath = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Full URL of your tt-rss installation. This should be set to the location of tt-rss directory, e.g. http://example.org/tt-rss/ You need to set this option correctly otherwise several features @@ -400,7 +400,7 @@ let feedCryptKey = mkOption { type = types.str; default = ""; - description = '' + description = lib.mdDoc '' Key used for encryption of passwords for password-protected feeds in the database. A string of 24 random characters. If left blank, encryption is not used. Requires mcrypt functions. @@ -413,7 +413,7 @@ let type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Operate in single user mode, disables all functionality related to multiple users and authentication. Enabling this assumes you have your tt-rss directory protected by other means (e.g. http auth). @@ -423,7 +423,7 @@ let simpleUpdateMode = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Enables fallback update mode where tt-rss tries to update feeds in background while tt-rss is open in your browser. If you don't have a lot of feeds and don't want to or can't run @@ -437,7 +437,7 @@ let forceArticlePurge = mkOption { type = types.int; default = 0; - description = '' + description = lib.mdDoc '' When this option is not 0, users ability to control feed purging intervals is disabled and all articles (which are not starred) older than this amount of days are purged. @@ -447,7 +447,7 @@ let enableGZipOutput = mkOption { type = types.bool; default = true; - description = '' + description = lib.mdDoc '' Selectively gzip output to improve wire performance. This requires PHP Zlib extension on the server. Enabling this can break tt-rss in several httpd/php configurations, @@ -459,7 +459,7 @@ let plugins = mkOption { type = types.listOf types.str; default = ["auth_internal" "note"]; - description = '' + description = lib.mdDoc '' List of plugins to load automatically for all users. System plugins have to be specified here. Please enable at least one authentication plugin here (auth_*). @@ -473,27 +473,27 @@ let pluginPackages = mkOption { type = types.listOf types.package; default = []; - description = '' + description = lib.mdDoc '' List of plugins to install. The list elements are expected to be derivations. All elements in this derivation are automatically - copied to the <literal>plugins.local</literal> directory. + copied to the `plugins.local` directory. ''; }; themePackages = mkOption { type = types.listOf types.package; default = []; - description = '' + description = lib.mdDoc '' List of themes to install. The list elements are expected to be derivations. All elements in this derivation are automatically - copied to the <literal>themes.local</literal> directory. + copied to the `themes.local` directory. ''; }; logDestination = mkOption { type = types.enum ["" "sql" "syslog"]; default = "sql"; - description = '' + description = lib.mdDoc '' Log destination to use. Possible values: sql (uses internal logging you can read in Preferences -> System), syslog - logs to system log. Setting this to blank uses PHP logging (usually to http server @@ -504,8 +504,8 @@ let extraConfig = mkOption { type = types.lines; default = ""; - description = '' - Additional lines to append to <literal>config.php</literal>. + description = lib.mdDoc '' + Additional lines to append to `config.php`. ''; }; }; @@ -534,6 +534,7 @@ let services.phpfpm.pools = mkIf (cfg.pool == "${poolName}") { ${poolName} = { inherit (cfg) user; + phpPackage = pkgs.php80; settings = mapAttrs (name: mkDefault) { "listen.owner" = "nginx"; "listen.group" = "nginx"; diff --git a/nixpkgs/nixos/modules/services/web-apps/vikunja.nix b/nixpkgs/nixos/modules/services/web-apps/vikunja.nix index 7575e96ca815..7db610159809 100644 --- a/nixpkgs/nixos/modules/services/web-apps/vikunja.nix +++ b/nixpkgs/nixos/modules/services/web-apps/vikunja.nix @@ -15,18 +15,18 @@ in { default = pkgs.vikunja-api; type = types.package; defaultText = literalExpression "pkgs.vikunja-api"; - description = "vikunja-api derivation to use."; + description = lib.mdDoc "vikunja-api derivation to use."; }; package-frontend = mkOption { default = pkgs.vikunja-frontend; type = types.package; defaultText = literalExpression "pkgs.vikunja-frontend"; - description = "vikunja-frontend derivation to use."; + description = lib.mdDoc "vikunja-frontend derivation to use."; }; environmentFiles = mkOption { type = types.listOf types.path; default = [ ]; - description = '' + description = lib.mdDoc '' List of environment files set in the vikunja systemd service. For example passwords should be set in one of these files. ''; @@ -35,34 +35,34 @@ in { type = types.bool; default = config.services.nginx.enable; defaultText = literalExpression "config.services.nginx.enable"; - description = '' + description = lib.mdDoc '' Whether to setup NGINX. Further nginx configuration can be done by changing - <option>services.nginx.virtualHosts.<frontendHostname></option>. + {option}`services.nginx.virtualHosts.<frontendHostname>`. This does not enable TLS or ACME by default. To enable this, set the - <option>services.nginx.virtualHosts.<frontendHostname>.enableACME</option> to - <literal>true</literal> and if appropriate do the same for - <option>services.nginx.virtualHosts.<frontendHostname>.forceSSL</option>. + {option}`services.nginx.virtualHosts.<frontendHostname>.enableACME` to + `true` and if appropriate do the same for + {option}`services.nginx.virtualHosts.<frontendHostname>.forceSSL`. ''; }; frontendScheme = mkOption { type = types.enum [ "http" "https" ]; - description = '' + description = lib.mdDoc '' Whether the site is available via http or https. This does not configure https or ACME in nginx! ''; }; frontendHostname = mkOption { type = types.str; - description = "The Hostname under which the frontend is running."; + description = lib.mdDoc "The Hostname under which the frontend is running."; }; settings = mkOption { type = format.type; default = {}; - description = '' + description = lib.mdDoc '' Vikunja configuration. Refer to - <link xlink:href="https://vikunja.io/docs/config-options/"/> + <https://vikunja.io/docs/config-options/> for details on supported values. ''; }; @@ -71,27 +71,27 @@ in { type = types.enum [ "sqlite" "mysql" "postgres" ]; example = "postgres"; default = "sqlite"; - description = "Database engine to use."; + description = lib.mdDoc "Database engine to use."; }; host = mkOption { type = types.str; default = "localhost"; - description = "Database host address. Can also be a socket."; + description = lib.mdDoc "Database host address. Can also be a socket."; }; user = mkOption { type = types.str; default = "vikunja"; - description = "Database user."; + description = lib.mdDoc "Database user."; }; database = mkOption { type = types.str; default = "vikunja"; - description = "Database name."; + description = lib.mdDoc "Database name."; }; path = mkOption { type = types.str; default = "/var/lib/vikunja/vikunja.db"; - description = "Path to the sqlite3 database file."; + description = lib.mdDoc "Path to the sqlite3 database file."; }; }; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/virtlyst.nix b/nixpkgs/nixos/modules/services/web-apps/virtlyst.nix deleted file mode 100644 index 37bdbb0e3b42..000000000000 --- a/nixpkgs/nixos/modules/services/web-apps/virtlyst.nix +++ /dev/null @@ -1,73 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - - cfg = config.services.virtlyst; - stateDir = "/var/lib/virtlyst"; - - ini = pkgs.writeText "virtlyst-config.ini" '' - [wsgi] - master = true - threads = auto - http-socket = ${cfg.httpSocket} - application = ${pkgs.virtlyst}/lib/libVirtlyst.so - chdir2 = ${stateDir} - static-map = /static=${pkgs.virtlyst}/root/static - - [Cutelyst] - production = true - DatabasePath = virtlyst.sqlite - TemplatePath = ${pkgs.virtlyst}/root/src - - [Rules] - cutelyst.* = true - virtlyst.* = true - ''; - -in - -{ - - options.services.virtlyst = { - enable = mkEnableOption "Virtlyst libvirt web interface"; - - adminPassword = mkOption { - type = types.str; - description = '' - Initial admin password with which the database will be seeded. - ''; - }; - - httpSocket = mkOption { - type = types.str; - default = "localhost:3000"; - description = '' - IP and/or port to which to bind the http socket. - ''; - }; - }; - - config = mkIf cfg.enable { - users.users.virtlyst = { - home = stateDir; - createHome = true; - group = mkIf config.virtualisation.libvirtd.enable "libvirtd"; - isSystemUser = true; - }; - - systemd.services.virtlyst = { - wantedBy = [ "multi-user.target" ]; - environment = { - VIRTLYST_ADMIN_PASSWORD = cfg.adminPassword; - }; - serviceConfig = { - ExecStart = "${pkgs.cutelyst}/bin/cutelyst-wsgi2 --ini ${ini}"; - User = "virtlyst"; - WorkingDirectory = stateDir; - }; - }; - }; - -} diff --git a/nixpkgs/nixos/modules/services/web-apps/whitebophir.nix b/nixpkgs/nixos/modules/services/web-apps/whitebophir.nix index f9db6fe379b0..c4dee3c6eec7 100644 --- a/nixpkgs/nixos/modules/services/web-apps/whitebophir.nix +++ b/nixpkgs/nixos/modules/services/web-apps/whitebophir.nix @@ -13,19 +13,19 @@ in { default = pkgs.whitebophir; defaultText = literalExpression "pkgs.whitebophir"; type = types.package; - description = "Whitebophir package to use."; + description = lib.mdDoc "Whitebophir package to use."; }; listenAddress = mkOption { type = types.str; default = "0.0.0.0"; - description = "Address to listen on (use 0.0.0.0 to allow access from any address)."; + description = lib.mdDoc "Address to listen on (use 0.0.0.0 to allow access from any address)."; }; port = mkOption { type = types.port; default = 5001; - description = "Port to bind to."; + description = lib.mdDoc "Port to bind to."; }; }; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/wiki-js.nix b/nixpkgs/nixos/modules/services/web-apps/wiki-js.nix index 1a6259dffeef..5dc0bb73259b 100644 --- a/nixpkgs/nixos/modules/services/web-apps/wiki-js.nix +++ b/nixpkgs/nixos/modules/services/web-apps/wiki-js.nix @@ -16,7 +16,7 @@ in { type = types.nullOr types.path; default = null; example = "/root/wiki-js.env"; - description = '' + description = lib.mdDoc '' Environment fiel to inject e.g. secrets into the configuration. ''; }; @@ -24,8 +24,8 @@ in { stateDirectoryName = mkOption { default = "wiki-js"; type = types.str; - description = '' - Name of the directory in <filename>/var/lib</filename>. + description = lib.mdDoc '' + Name of the directory in {file}`/var/lib`. ''; }; @@ -37,7 +37,7 @@ in { port = mkOption { type = types.port; default = 3000; - description = '' + description = lib.mdDoc '' TCP port the process should listen to. ''; }; @@ -45,7 +45,7 @@ in { bindIP = mkOption { default = "0.0.0.0"; type = types.str; - description = '' + description = lib.mdDoc '' IPs the service should listen to. ''; }; @@ -64,14 +64,14 @@ in { host = mkOption { type = types.str; example = "/run/postgresql"; - description = '' + description = lib.mdDoc '' Hostname or socket-path to connect to. ''; }; db = mkOption { default = "wiki"; type = types.str; - description = '' + description = lib.mdDoc '' Name of the database to use. ''; }; @@ -80,7 +80,7 @@ in { logLevel = mkOption { default = "info"; type = types.enum [ "error" "warn" "info" "verbose" "debug" "silly" ]; - description = '' + description = lib.mdDoc '' Define how much detail is supposed to be logged at runtime. ''; }; @@ -95,12 +95,11 @@ in { }; description = '' Settings to configure <package>wiki-js</package>. This directly - corresponds to <link xlink:href="https://docs.requarks.io/install/config">the upstream - configuration options</link>. + corresponds to <link xlink:href="https://docs.requarks.io/install/config">the upstream configuration options</link>. Secrets can be injected via the environment by <itemizedlist> - <listitem><para>specifying <xref linkend="opt-services.wiki-js.environmentFile" /> + <listitem><para>specifying <xref linkend="opt-services.wiki-js.environmentFile"/> to contain secrets</para></listitem> <listitem><para>and setting sensitive values to <literal>$(ENVIRONMENT_VAR)</literal> with this value defined in the environment-file.</para></listitem> diff --git a/nixpkgs/nixos/modules/services/web-apps/wordpress.nix b/nixpkgs/nixos/modules/services/web-apps/wordpress.nix index 59471a739cbb..c841ded353e7 100644 --- a/nixpkgs/nixos/modules/services/web-apps/wordpress.nix +++ b/nixpkgs/nixos/modules/services/web-apps/wordpress.nix @@ -82,13 +82,13 @@ let type = types.package; default = pkgs.wordpress; defaultText = literalExpression "pkgs.wordpress"; - description = "Which WordPress package to use."; + description = lib.mdDoc "Which WordPress package to use."; }; uploadsDir = mkOption { type = types.path; default = "/var/lib/wordpress/${name}/uploads"; - description = '' + description = lib.mdDoc '' This directory is used for uploads of pictures. The directory passed here is automatically created and permissions adjusted as required. ''; @@ -152,47 +152,47 @@ let host = mkOption { type = types.str; default = "localhost"; - description = "Database host address."; + description = lib.mdDoc "Database host address."; }; port = mkOption { type = types.port; default = 3306; - description = "Database host port."; + description = lib.mdDoc "Database host port."; }; name = mkOption { type = types.str; default = "wordpress"; - description = "Database name."; + description = lib.mdDoc "Database name."; }; user = mkOption { type = types.str; default = "wordpress"; - description = "Database user."; + description = lib.mdDoc "Database user."; }; passwordFile = mkOption { type = types.nullOr types.path; default = null; example = "/run/keys/wordpress-dbpassword"; - description = '' + description = lib.mdDoc '' A file containing the password corresponding to - <option>database.user</option>. + {option}`database.user`. ''; }; tablePrefix = mkOption { type = types.str; default = "wp_"; - description = '' + description = lib.mdDoc '' The $table_prefix is the value placed in the front of your database tables. Change the value if you want to use something other than wp_ for your database prefix. Typically this is changed if you are installing multiple WordPress blogs in the same database. - See <link xlink:href='https://codex.wordpress.org/Editing_wp-config.php#table_prefix'/>. + See <https://codex.wordpress.org/Editing_wp-config.php#table_prefix>. ''; }; @@ -200,13 +200,13 @@ let type = types.nullOr types.path; default = null; defaultText = literalExpression "/run/mysqld/mysqld.sock"; - description = "Path to the unix socket file to use for authentication."; + description = lib.mdDoc "Path to the unix socket file to use for authentication."; }; createLocally = mkOption { type = types.bool; default = true; - description = "Create the database and database user locally."; + description = lib.mdDoc "Create the database and database user locally."; }; }; @@ -219,8 +219,8 @@ let enableACME = true; } ''; - description = '' - Apache configuration can be done by adapting <option>services.httpd.virtualHosts</option>. + description = lib.mdDoc '' + Apache configuration can be done by adapting {option}`services.httpd.virtualHosts`. ''; }; @@ -234,8 +234,8 @@ let "pm.max_spare_servers" = 4; "pm.max_requests" = 500; }; - description = '' - Options for the WordPress PHP pool. See the documentation on <literal>php-fpm.conf</literal> + description = lib.mdDoc '' + Options for the WordPress PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives. ''; }; @@ -243,10 +243,10 @@ let extraConfig = mkOption { type = types.lines; default = ""; - description = '' + description = lib.mdDoc '' Any additional text to be appended to the wp-config.php configuration file. This is a PHP script. For configuration - settings, see <link xlink:href='https://codex.wordpress.org/Editing_wp-config.php'/>. + settings, see <https://codex.wordpress.org/Editing_wp-config.php>. ''; example = '' define( 'AUTOSAVE_INTERVAL', 60 ); // Seconds @@ -265,20 +265,20 @@ in sites = mkOption { type = types.attrsOf (types.submodule siteOpts); default = {}; - description = "Specification of one or more WordPress sites to serve"; + description = lib.mdDoc "Specification of one or more WordPress sites to serve"; }; webserver = mkOption { type = types.enum [ "httpd" "nginx" "caddy" ]; default = "httpd"; - description = '' + description = lib.mdDoc '' Whether to use apache2 or nginx for virtual host management. - Further nginx configuration can be done by adapting <literal>services.nginx.virtualHosts.<name></literal>. - See <xref linkend="opt-services.nginx.virtualHosts"/> for further information. + Further nginx configuration can be done by adapting `services.nginx.virtualHosts.<name>`. + See [](#opt-services.nginx.virtualHosts) for further information. - Further apache2 configuration can be done by adapting <literal>services.httpd.virtualHosts.<name></literal>. - See <xref linkend="opt-services.httpd.virtualHosts"/> for further information. + Further apache2 configuration can be done by adapting `services.httpd.virtualHosts.<name>`. + See [](#opt-services.httpd.virtualHosts) for further information. ''; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/youtrack.nix b/nixpkgs/nixos/modules/services/web-apps/youtrack.nix index b83265ffeab6..789880d61f61 100644 --- a/nixpkgs/nixos/modules/services/web-apps/youtrack.nix +++ b/nixpkgs/nixos/modules/services/web-apps/youtrack.nix @@ -24,7 +24,7 @@ in enable = mkEnableOption "YouTrack service"; address = mkOption { - description = '' + description = lib.mdDoc '' The interface youtrack will listen on. ''; default = "127.0.0.1"; @@ -32,7 +32,7 @@ in }; baseUrl = mkOption { - description = '' + description = lib.mdDoc '' Base URL for youtrack. Will be auto-detected and stored in database. ''; type = types.nullOr types.str; @@ -41,7 +41,7 @@ in extraParams = mkOption { default = {}; - description = '' + description = lib.mdDoc '' Extra parameters to pass to youtrack. See https://www.jetbrains.com/help/youtrack/standalone/YouTrack-Java-Start-Parameters.html for more information. @@ -55,7 +55,7 @@ in }; package = mkOption { - description = '' + description = lib.mdDoc '' Package to use. ''; type = types.package; @@ -64,7 +64,7 @@ in }; port = mkOption { - description = '' + description = lib.mdDoc '' The port youtrack will listen on. ''; default = 8080; @@ -72,7 +72,7 @@ in }; statePath = mkOption { - description = '' + description = lib.mdDoc '' Where to keep the youtrack database. ''; type = types.path; @@ -80,7 +80,7 @@ in }; virtualHost = mkOption { - description = '' + description = lib.mdDoc '' Name of the nginx virtual host to use and setup. If null, do not setup anything. ''; @@ -89,7 +89,7 @@ in }; jvmOpts = mkOption { - description = '' + description = lib.mdDoc '' Extra options to pass to the JVM. See https://www.jetbrains.com/help/youtrack/standalone/Configure-JVM-Options.html for more information. @@ -100,7 +100,7 @@ in }; maxMemory = mkOption { - description = '' + description = lib.mdDoc '' Maximum Java heap size ''; type = types.str; @@ -108,7 +108,7 @@ in }; maxMetaspaceSize = mkOption { - description = '' + description = lib.mdDoc '' Maximum java Metaspace memory. ''; type = types.str; diff --git a/nixpkgs/nixos/modules/services/web-apps/zabbix.nix b/nixpkgs/nixos/modules/services/web-apps/zabbix.nix index 538dac0d5be2..c6ac809a73b0 100644 --- a/nixpkgs/nixos/modules/services/web-apps/zabbix.nix +++ b/nixpkgs/nixos/modules/services/web-apps/zabbix.nix @@ -46,19 +46,19 @@ in type = types.package; default = pkgs.zabbix.web; defaultText = literalExpression "zabbix.web"; - description = "Which Zabbix package to use."; + description = lib.mdDoc "Which Zabbix package to use."; }; server = { port = mkOption { type = types.int; - description = "The port of the Zabbix server to connect to."; + description = lib.mdDoc "The port of the Zabbix server to connect to."; default = 10051; }; address = mkOption { type = types.str; - description = "The IP address or hostname of the Zabbix server to connect to."; + description = lib.mdDoc "The IP address or hostname of the Zabbix server to connect to."; default = "localhost"; }; }; @@ -68,13 +68,13 @@ in type = types.enum [ "mysql" "pgsql" "oracle" ]; example = "mysql"; default = "pgsql"; - description = "Database engine to use."; + description = lib.mdDoc "Database engine to use."; }; host = mkOption { type = types.str; default = ""; - description = "Database host address."; + description = lib.mdDoc "Database host address."; }; port = mkOption { @@ -88,28 +88,28 @@ in else if config.${opt.database.type} == "pgsql" then config.${options.services.postgresql.port} else 1521 ''; - description = "Database host port."; + description = lib.mdDoc "Database host port."; }; name = mkOption { type = types.str; default = "zabbix"; - description = "Database name."; + description = lib.mdDoc "Database name."; }; user = mkOption { type = types.str; default = "zabbix"; - description = "Database user."; + description = lib.mdDoc "Database user."; }; passwordFile = mkOption { type = types.nullOr types.path; default = null; example = "/run/keys/zabbix-dbpassword"; - description = '' + description = lib.mdDoc '' A file containing the password corresponding to - <option>database.user</option>. + {option}`database.user`. ''; }; @@ -117,7 +117,7 @@ in type = types.nullOr types.path; default = null; example = "/run/postgresql"; - description = "Path to the unix socket file to use for authentication."; + description = lib.mdDoc "Path to the unix socket file to use for authentication."; }; }; @@ -131,9 +131,9 @@ in enableACME = true; } ''; - description = '' - Apache configuration can be done by adapting <literal>services.httpd.virtualHosts.<name></literal>. - See <xref linkend="opt-services.httpd.virtualHosts"/> for further information. + description = lib.mdDoc '' + Apache configuration can be done by adapting `services.httpd.virtualHosts.<name>`. + See [](#opt-services.httpd.virtualHosts) for further information. ''; }; @@ -147,16 +147,16 @@ in "pm.max_spare_servers" = 4; "pm.max_requests" = 500; }; - description = '' - Options for the Zabbix PHP pool. See the documentation on <literal>php-fpm.conf</literal> for details on configuration directives. + description = lib.mdDoc '' + Options for the Zabbix PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives. ''; }; extraConfig = mkOption { type = types.lines; default = ""; - description = '' - Additional configuration to be copied verbatim into <filename>zabbix.conf.php</filename>. + description = lib.mdDoc '' + Additional configuration to be copied verbatim into {file}`zabbix.conf.php`. ''; }; |