aboutsummaryrefslogtreecommitdiff
path: root/nixpkgs/nixos/modules/services/web-servers
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos/modules/services/web-servers')
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix719
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix180
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/caddy.nix105
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/darkhttpd.nix77
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/fcgiwrap.nix72
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/hitch/default.nix108
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/hydron.nix165
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/jboss/builder.sh72
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/jboss/default.nix80
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/lighttpd/cgit.nix91
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/lighttpd/collectd.nix58
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/lighttpd/default.nix256
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/lighttpd/gitweb.nix52
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/meguca.nix174
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/mighttpd2.nix132
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/minio.nix108
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/nginx/default.nix709
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/nginx/gitweb.nix61
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/nginx/location-options.nix95
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/nginx/vhost-options.nix228
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/phpfpm/default.nix279
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/shellinabox.nix122
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/tomcat.nix425
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/traefik.nix124
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/unit/default.nix125
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/uwsgi.nix160
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/varnish/default.nix113
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/zope2.nix258
28 files changed, 5148 insertions, 0 deletions
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix
new file mode 100644
index 00000000000..098160ee369
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix
@@ -0,0 +1,719 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ mainCfg = config.services.httpd;
+
+ httpd = mainCfg.package.out;
+
+ httpdConf = mainCfg.configFile;
+
+ php = mainCfg.phpPackage.override { apacheHttpd = httpd.dev; /* otherwise it only gets .out */ };
+
+ phpMajorVersion = head (splitString "." php.version);
+
+ mod_perl = pkgs.apacheHttpdPackages.mod_perl.override { apacheHttpd = httpd; };
+
+ defaultListen = cfg: if cfg.enableSSL
+ then [{ip = "*"; port = 443;}]
+ else [{ip = "*"; port = 80;}];
+
+ getListen = cfg:
+ if cfg.listen == []
+ then defaultListen cfg
+ else cfg.listen;
+
+ listenToString = l: "${l.ip}:${toString l.port}";
+
+ extraModules = attrByPath ["extraModules"] [] mainCfg;
+ extraForeignModules = filter isAttrs extraModules;
+ extraApacheModules = filter isString extraModules;
+
+
+ makeServerInfo = cfg: {
+ # Canonical name must not include a trailing slash.
+ canonicalNames =
+ let defaultPort = (head (defaultListen cfg)).port; in
+ map (port:
+ (if cfg.enableSSL then "https" else "http") + "://" +
+ cfg.hostName +
+ (if port != defaultPort then ":${toString port}" else "")
+ ) (map (x: x.port) (getListen cfg));
+
+ # Admin address: inherit from the main server if not specified for
+ # a virtual host.
+ adminAddr = if cfg.adminAddr != null then cfg.adminAddr else mainCfg.adminAddr;
+
+ vhostConfig = cfg;
+ serverConfig = mainCfg;
+ fullConfig = config; # machine config
+ };
+
+
+ allHosts = [mainCfg] ++ mainCfg.virtualHosts;
+
+
+ callSubservices = serverInfo: defs:
+ let f = svc:
+ let
+ svcFunction =
+ if svc ? function then svc.function
+ # instead of using serviceType="mediawiki"; you can copy mediawiki.nix to any location outside nixpkgs, modify it at will, and use serviceExpression=./mediawiki.nix;
+ else if svc ? serviceExpression then import (toString svc.serviceExpression)
+ else import (toString "${toString ./.}/${if svc ? serviceType then svc.serviceType else svc.serviceName}.nix");
+ config = (evalModules
+ { modules = [ { options = res.options; config = svc.config or svc; } ];
+ check = false;
+ }).config;
+ defaults = {
+ extraConfig = "";
+ extraModules = [];
+ extraModulesPre = [];
+ extraPath = [];
+ extraServerPath = [];
+ globalEnvVars = [];
+ robotsEntries = "";
+ startupScript = "";
+ enablePHP = false;
+ enablePerl = false;
+ phpOptions = "";
+ options = {};
+ documentRoot = null;
+ };
+ res = defaults // svcFunction { inherit config lib pkgs serverInfo php; };
+ in res;
+ in map f defs;
+
+
+ # !!! callSubservices is expensive
+ subservicesFor = cfg: callSubservices (makeServerInfo cfg) cfg.extraSubservices;
+
+ mainSubservices = subservicesFor mainCfg;
+
+ allSubservices = mainSubservices ++ concatMap subservicesFor mainCfg.virtualHosts;
+
+
+ enableSSL = any (vhost: vhost.enableSSL) allHosts;
+
+
+ # Names of modules from ${httpd}/modules that we want to load.
+ apacheModules =
+ [ # HTTP authentication mechanisms: basic and digest.
+ "auth_basic" "auth_digest"
+
+ # Authentication: is the user who he claims to be?
+ "authn_file" "authn_dbm" "authn_anon" "authn_core"
+
+ # Authorization: is the user allowed access?
+ "authz_user" "authz_groupfile" "authz_host" "authz_core"
+
+ # Other modules.
+ "ext_filter" "include" "log_config" "env" "mime_magic"
+ "cern_meta" "expires" "headers" "usertrack" /* "unique_id" */ "setenvif"
+ "mime" "dav" "status" "autoindex" "asis" "info" "dav_fs"
+ "vhost_alias" "negotiation" "dir" "imagemap" "actions" "speling"
+ "userdir" "alias" "rewrite" "proxy" "proxy_http"
+ "unixd" "cache" "cache_disk" "slotmem_shm" "socache_shmcb"
+ "mpm_${mainCfg.multiProcessingModule}"
+
+ # For compatibility with old configurations, the new module mod_access_compat is provided.
+ "access_compat"
+ ]
+ ++ (if mainCfg.multiProcessingModule == "prefork" then [ "cgi" ] else [ "cgid" ])
+ ++ optional enableSSL "ssl"
+ ++ extraApacheModules;
+
+
+ allDenied = "Require all denied";
+ allGranted = "Require all granted";
+
+
+ loggingConf = (if mainCfg.logFormat != "none" then ''
+ ErrorLog ${mainCfg.logDir}/error.log
+
+ LogLevel notice
+
+ LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
+ LogFormat "%h %l %u %t \"%r\" %>s %b" common
+ LogFormat "%{Referer}i -> %U" referer
+ LogFormat "%{User-agent}i" agent
+
+ CustomLog ${mainCfg.logDir}/access.log ${mainCfg.logFormat}
+ '' else ''
+ ErrorLog /dev/null
+ '');
+
+
+ browserHacks = ''
+ BrowserMatch "Mozilla/2" nokeepalive
+ BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0
+ BrowserMatch "RealPlayer 4\.0" force-response-1.0
+ BrowserMatch "Java/1\.0" force-response-1.0
+ BrowserMatch "JDK/1\.0" force-response-1.0
+ BrowserMatch "Microsoft Data Access Internet Publishing Provider" redirect-carefully
+ BrowserMatch "^WebDrive" redirect-carefully
+ BrowserMatch "^WebDAVFS/1.[012]" redirect-carefully
+ BrowserMatch "^gnome-vfs" redirect-carefully
+ '';
+
+
+ sslConf = ''
+ SSLSessionCache shmcb:${mainCfg.stateDir}/ssl_scache(512000)
+
+ Mutex posixsem
+
+ SSLRandomSeed startup builtin
+ SSLRandomSeed connect builtin
+
+ SSLProtocol ${mainCfg.sslProtocols}
+ SSLCipherSuite ${mainCfg.sslCiphers}
+ SSLHonorCipherOrder on
+ '';
+
+
+ mimeConf = ''
+ TypesConfig ${httpd}/conf/mime.types
+
+ AddType application/x-x509-ca-cert .crt
+ AddType application/x-pkcs7-crl .crl
+ AddType application/x-httpd-php .php .phtml
+
+ <IfModule mod_mime_magic.c>
+ MIMEMagicFile ${httpd}/conf/magic
+ </IfModule>
+ '';
+
+
+ perServerConf = isMainServer: cfg: let
+
+ serverInfo = makeServerInfo cfg;
+
+ subservices = callSubservices serverInfo cfg.extraSubservices;
+
+ maybeDocumentRoot = fold (svc: acc:
+ if acc == null then svc.documentRoot else assert svc.documentRoot == null; acc
+ ) null ([ cfg ] ++ subservices);
+
+ documentRoot = if maybeDocumentRoot != null then maybeDocumentRoot else
+ pkgs.runCommand "empty" { preferLocalBuild = true; } "mkdir -p $out";
+
+ documentRootConf = ''
+ DocumentRoot "${documentRoot}"
+
+ <Directory "${documentRoot}">
+ Options Indexes FollowSymLinks
+ AllowOverride None
+ ${allGranted}
+ </Directory>
+ '';
+
+ robotsTxt =
+ concatStringsSep "\n" (filter (x: x != "") (
+ # If this is a vhost, the include the entries for the main server as well.
+ (if isMainServer then [] else [mainCfg.robotsEntries] ++ map (svc: svc.robotsEntries) mainSubservices)
+ ++ [cfg.robotsEntries]
+ ++ (map (svc: svc.robotsEntries) subservices)));
+
+ in ''
+ ${concatStringsSep "\n" (map (n: "ServerName ${n}") serverInfo.canonicalNames)}
+
+ ${concatMapStrings (alias: "ServerAlias ${alias}\n") cfg.serverAliases}
+
+ ${if cfg.sslServerCert != null then ''
+ SSLCertificateFile ${cfg.sslServerCert}
+ SSLCertificateKeyFile ${cfg.sslServerKey}
+ ${if cfg.sslServerChain != null then ''
+ SSLCertificateChainFile ${cfg.sslServerChain}
+ '' else ""}
+ '' else ""}
+
+ ${if cfg.enableSSL then ''
+ SSLEngine on
+ '' else if enableSSL then /* i.e., SSL is enabled for some host, but not this one */
+ ''
+ SSLEngine off
+ '' else ""}
+
+ ${if isMainServer || cfg.adminAddr != null then ''
+ ServerAdmin ${cfg.adminAddr}
+ '' else ""}
+
+ ${if !isMainServer && mainCfg.logPerVirtualHost then ''
+ ErrorLog ${mainCfg.logDir}/error-${cfg.hostName}.log
+ CustomLog ${mainCfg.logDir}/access-${cfg.hostName}.log ${cfg.logFormat}
+ '' else ""}
+
+ ${optionalString (robotsTxt != "") ''
+ Alias /robots.txt ${pkgs.writeText "robots.txt" robotsTxt}
+ ''}
+
+ ${if isMainServer || maybeDocumentRoot != null then documentRootConf else ""}
+
+ ${if cfg.enableUserDir then ''
+
+ UserDir public_html
+ UserDir disabled root
+
+ <Directory "/home/*/public_html">
+ AllowOverride FileInfo AuthConfig Limit Indexes
+ Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
+ <Limit GET POST OPTIONS>
+ ${allGranted}
+ </Limit>
+ <LimitExcept GET POST OPTIONS>
+ ${allDenied}
+ </LimitExcept>
+ </Directory>
+
+ '' else ""}
+
+ ${if cfg.globalRedirect != null && cfg.globalRedirect != "" then ''
+ RedirectPermanent / ${cfg.globalRedirect}
+ '' else ""}
+
+ ${
+ let makeFileConf = elem: ''
+ Alias ${elem.urlPath} ${elem.file}
+ '';
+ in concatMapStrings makeFileConf cfg.servedFiles
+ }
+
+ ${
+ let makeDirConf = elem: ''
+ Alias ${elem.urlPath} ${elem.dir}/
+ <Directory ${elem.dir}>
+ Options +Indexes
+ ${allGranted}
+ AllowOverride All
+ </Directory>
+ '';
+ in concatMapStrings makeDirConf cfg.servedDirs
+ }
+
+ ${concatMapStrings (svc: svc.extraConfig) subservices}
+
+ ${cfg.extraConfig}
+ '';
+
+
+ confFile = pkgs.writeText "httpd.conf" ''
+
+ ServerRoot ${httpd}
+
+ DefaultRuntimeDir ${mainCfg.stateDir}/runtime
+
+ PidFile ${mainCfg.stateDir}/httpd.pid
+
+ ${optionalString (mainCfg.multiProcessingModule != "prefork") ''
+ # mod_cgid requires this.
+ ScriptSock ${mainCfg.stateDir}/cgisock
+ ''}
+
+ <IfModule prefork.c>
+ MaxClients ${toString mainCfg.maxClients}
+ MaxRequestsPerChild ${toString mainCfg.maxRequestsPerChild}
+ </IfModule>
+
+ ${let
+ listen = concatMap getListen allHosts;
+ toStr = listen: "Listen ${listenToString listen}\n";
+ uniqueListen = uniqList {inputList = map toStr listen;};
+ in concatStrings uniqueListen
+ }
+
+ User ${mainCfg.user}
+ Group ${mainCfg.group}
+
+ ${let
+ load = {name, path}: "LoadModule ${name}_module ${path}\n";
+ allModules =
+ concatMap (svc: svc.extraModulesPre) allSubservices
+ ++ map (name: {inherit name; path = "${httpd}/modules/mod_${name}.so";}) apacheModules
+ ++ optional mainCfg.enableMellon { name = "auth_mellon"; path = "${pkgs.apacheHttpdPackages.mod_auth_mellon}/modules/mod_auth_mellon.so"; }
+ ++ optional enablePHP { name = "php${phpMajorVersion}"; path = "${php}/modules/libphp${phpMajorVersion}.so"; }
+ ++ optional enablePerl { name = "perl"; path = "${mod_perl}/modules/mod_perl.so"; }
+ ++ concatMap (svc: svc.extraModules) allSubservices
+ ++ extraForeignModules;
+ in concatMapStrings load (unique allModules)
+ }
+
+ AddHandler type-map var
+
+ <Files ~ "^\.ht">
+ ${allDenied}
+ </Files>
+
+ ${mimeConf}
+ ${loggingConf}
+ ${browserHacks}
+
+ Include ${httpd}/conf/extra/httpd-default.conf
+ Include ${httpd}/conf/extra/httpd-autoindex.conf
+ Include ${httpd}/conf/extra/httpd-multilang-errordoc.conf
+ Include ${httpd}/conf/extra/httpd-languages.conf
+
+ TraceEnable off
+
+ ${if enableSSL then sslConf else ""}
+
+ # Fascist default - deny access to everything.
+ <Directory />
+ Options FollowSymLinks
+ AllowOverride None
+ ${allDenied}
+ </Directory>
+
+ # But do allow access to files in the store so that we don't have
+ # to generate <Directory> clauses for every generated file that we
+ # want to serve.
+ <Directory /nix/store>
+ ${allGranted}
+ </Directory>
+
+ # Generate directives for the main server.
+ ${perServerConf true mainCfg}
+
+ ${let
+ makeVirtualHost = vhost: ''
+ <VirtualHost ${concatStringsSep " " (map listenToString (getListen vhost))}>
+ ${perServerConf false vhost}
+ </VirtualHost>
+ '';
+ in concatMapStrings makeVirtualHost mainCfg.virtualHosts
+ }
+ '';
+
+
+ enablePHP = mainCfg.enablePHP || any (svc: svc.enablePHP) allSubservices;
+
+ enablePerl = mainCfg.enablePerl || any (svc: svc.enablePerl) allSubservices;
+
+
+ # Generate the PHP configuration file. Should probably be factored
+ # out into a separate module.
+ phpIni = pkgs.runCommand "php.ini"
+ { options = concatStringsSep "\n"
+ ([ mainCfg.phpOptions ] ++ (map (svc: svc.phpOptions) allSubservices));
+ preferLocalBuild = true;
+ }
+ ''
+ cat ${php}/etc/php.ini > $out
+ echo "$options" >> $out
+ '';
+
+in
+
+
+{
+
+ ###### interface
+
+ options = {
+
+ services.httpd = {
+
+ enable = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Whether to enable the Apache HTTP Server.";
+ };
+
+ package = mkOption {
+ type = types.package;
+ default = pkgs.apacheHttpd;
+ defaultText = "pkgs.apacheHttpd";
+ description = ''
+ Overridable attribute of the Apache HTTP Server package to use.
+ '';
+ };
+
+ configFile = mkOption {
+ type = types.path;
+ default = confFile;
+ defaultText = "confFile";
+ example = literalExample ''pkgs.writeText "httpd.conf" "# my custom config file ..."'';
+ description = ''
+ Override the configuration file used by Apache. By default,
+ NixOS generates one automatically.
+ '';
+ };
+
+ extraConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = ''
+ Cnfiguration lines appended to the generated Apache
+ configuration file. Note that this mechanism may not work
+ when <option>configFile</option> is overridden.
+ '';
+ };
+
+ extraModules = mkOption {
+ type = types.listOf types.unspecified;
+ default = [];
+ example = literalExample ''[ "proxy_connect" { name = "php5"; path = "''${pkgs.php}/modules/libphp5.so"; } ]'';
+ description = ''
+ Additional Apache modules to be used. These can be
+ specified as a string in the case of modules distributed
+ with Apache, or as an attribute set specifying the
+ <varname>name</varname> and <varname>path</varname> of the
+ module.
+ '';
+ };
+
+ logPerVirtualHost = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ If enabled, each virtual host gets its own
+ <filename>access.log</filename> and
+ <filename>error.log</filename>, namely suffixed by the
+ <option>hostName</option> of the virtual host.
+ '';
+ };
+
+ user = mkOption {
+ type = types.str;
+ default = "wwwrun";
+ description = ''
+ User account under which httpd runs. The account is created
+ automatically if it doesn't exist.
+ '';
+ };
+
+ group = mkOption {
+ type = types.str;
+ default = "wwwrun";
+ description = ''
+ Group under which httpd runs. The account is created
+ automatically if it doesn't exist.
+ '';
+ };
+
+ logDir = mkOption {
+ type = types.path;
+ default = "/var/log/httpd";
+ description = ''
+ Directory for Apache's log files. It is created automatically.
+ '';
+ };
+
+ stateDir = mkOption {
+ type = types.path;
+ default = "/run/httpd";
+ description = ''
+ Directory for Apache's transient runtime state (such as PID
+ files). It is created automatically. Note that the default,
+ <filename>/run/httpd</filename>, is deleted at boot time.
+ '';
+ };
+
+ virtualHosts = mkOption {
+ type = types.listOf (types.submodule (
+ { options = import ./per-server-options.nix {
+ inherit lib;
+ forMainServer = false;
+ };
+ }));
+ default = [];
+ example = [
+ { hostName = "foo";
+ documentRoot = "/data/webroot-foo";
+ }
+ { hostName = "bar";
+ documentRoot = "/data/webroot-bar";
+ }
+ ];
+ description = ''
+ Specification of the virtual hosts served by Apache. Each
+ element should be an attribute set specifying the
+ configuration of the virtual host. The available options
+ are the non-global options permissible for the main host.
+ '';
+ };
+
+ enableMellon = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Whether to enable the mod_auth_mellon module.";
+ };
+
+ enablePHP = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Whether to enable the PHP module.";
+ };
+
+ phpPackage = mkOption {
+ type = types.package;
+ default = pkgs.php;
+ defaultText = "pkgs.php";
+ description = ''
+ Overridable attribute of the PHP package to use.
+ '';
+ };
+
+ enablePerl = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Whether to enable the Perl module (mod_perl).";
+ };
+
+ phpOptions = mkOption {
+ type = types.lines;
+ default = "";
+ example =
+ ''
+ date.timezone = "CET"
+ '';
+ description =
+ "Options appended to the PHP configuration file <filename>php.ini</filename>.";
+ };
+
+ multiProcessingModule = mkOption {
+ type = types.str;
+ default = "prefork";
+ example = "worker";
+ description =
+ ''
+ Multi-processing module to be used by Apache. Available
+ modules are <literal>prefork</literal> (the default;
+ handles each request in a separate child process),
+ <literal>worker</literal> (hybrid approach that starts a
+ number of child processes each running a number of
+ threads) and <literal>event</literal> (a recent variant of
+ <literal>worker</literal> that handles persistent
+ connections more efficiently).
+ '';
+ };
+
+ maxClients = mkOption {
+ type = types.int;
+ default = 150;
+ example = 8;
+ description = "Maximum number of httpd processes (prefork)";
+ };
+
+ maxRequestsPerChild = mkOption {
+ type = types.int;
+ default = 0;
+ example = 500;
+ description =
+ "Maximum number of httpd requests answered per httpd child (prefork), 0 means unlimited";
+ };
+
+ sslCiphers = mkOption {
+ type = types.str;
+ default = "HIGH:!aNULL:!MD5:!EXP";
+ description = "Cipher Suite available for negotiation in SSL proxy handshake.";
+ };
+
+ sslProtocols = mkOption {
+ type = types.str;
+ default = "All -SSLv2 -SSLv3 -TLSv1";
+ example = "All -SSLv2 -SSLv3";
+ description = "Allowed SSL/TLS protocol versions.";
+ };
+ }
+
+ # Include the options shared between the main server and virtual hosts.
+ // (import ./per-server-options.nix {
+ inherit lib;
+ forMainServer = true;
+ });
+
+ };
+
+
+ ###### implementation
+
+ config = mkIf config.services.httpd.enable {
+
+ assertions = [ { assertion = mainCfg.enableSSL == true
+ -> mainCfg.sslServerCert != null
+ && mainCfg.sslServerKey != null;
+ message = "SSL is enabled for httpd, but sslServerCert and/or sslServerKey haven't been specified."; }
+ ];
+
+ warnings = map (cfg: "apache-httpd's extraSubservices option is deprecated. Most existing subservices have been ported to the NixOS module system. Please update your configuration accordingly.") (lib.filter (cfg: cfg.extraSubservices != []) allHosts);
+
+ users.users = optionalAttrs (mainCfg.user == "wwwrun") (singleton
+ { name = "wwwrun";
+ group = mainCfg.group;
+ description = "Apache httpd user";
+ uid = config.ids.uids.wwwrun;
+ });
+
+ users.groups = optionalAttrs (mainCfg.group == "wwwrun") (singleton
+ { name = "wwwrun";
+ gid = config.ids.gids.wwwrun;
+ });
+
+ environment.systemPackages = [httpd] ++ concatMap (svc: svc.extraPath) allSubservices;
+
+ services.httpd.phpOptions =
+ ''
+ ; Needed for PHP's mail() function.
+ sendmail_path = sendmail -t -i
+
+ ; Don't advertise PHP
+ expose_php = off
+ '' + optionalString (config.time.timeZone != null) ''
+
+ ; Apparently PHP doesn't use $TZ.
+ date.timezone = "${config.time.timeZone}"
+ '';
+
+ systemd.services.httpd =
+ { description = "Apache HTTPD";
+
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.target" "fs.target" ];
+
+ path =
+ [ httpd pkgs.coreutils pkgs.gnugrep ]
+ ++ optional enablePHP pkgs.system-sendmail # Needed for PHP's mail() function.
+ ++ concatMap (svc: svc.extraServerPath) allSubservices;
+
+ environment =
+ optionalAttrs enablePHP { PHPRC = phpIni; }
+ // optionalAttrs mainCfg.enableMellon { LD_LIBRARY_PATH = "${pkgs.xmlsec}/lib"; }
+ // (listToAttrs (concatMap (svc: svc.globalEnvVars) allSubservices));
+
+ preStart =
+ ''
+ mkdir -m 0750 -p ${mainCfg.stateDir}
+ [ $(id -u) != 0 ] || chown root.${mainCfg.group} ${mainCfg.stateDir}
+
+ mkdir -m 0750 -p "${mainCfg.stateDir}/runtime"
+ [ $(id -u) != 0 ] || chown root.${mainCfg.group} "${mainCfg.stateDir}/runtime"
+
+ mkdir -m 0700 -p ${mainCfg.logDir}
+
+ # Get rid of old semaphores. These tend to accumulate across
+ # server restarts, eventually preventing it from restarting
+ # successfully.
+ for i in $(${pkgs.utillinux}/bin/ipcs -s | grep ' ${mainCfg.user} ' | cut -f2 -d ' '); do
+ ${pkgs.utillinux}/bin/ipcrm -s $i
+ done
+
+ # Run the startup hooks for the subservices.
+ for i in ${toString (map (svn: svn.startupScript) allSubservices)}; do
+ echo Running Apache startup hook $i...
+ $i
+ done
+ '';
+
+ serviceConfig.ExecStart = "@${httpd}/bin/httpd httpd -f ${httpdConf}";
+ serviceConfig.ExecStop = "${httpd}/bin/httpd -f ${httpdConf} -k graceful-stop";
+ serviceConfig.ExecReload = "${httpd}/bin/httpd -f ${httpdConf} -k graceful";
+ serviceConfig.Type = "forking";
+ serviceConfig.PIDFile = "${mainCfg.stateDir}/httpd.pid";
+ serviceConfig.Restart = "always";
+ serviceConfig.RestartSec = "5s";
+ };
+
+ };
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix
new file mode 100644
index 00000000000..9d747549c27
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix
@@ -0,0 +1,180 @@
+# This file defines the options that can be used both for the Apache
+# main server configuration, and for the virtual hosts. (The latter
+# has additional options that affect the web server as a whole, like
+# the user/group to run under.)
+
+{ forMainServer, lib }:
+
+with lib;
+
+{
+
+ hostName = mkOption {
+ type = types.str;
+ default = "localhost";
+ description = "Canonical hostname for the server.";
+ };
+
+ serverAliases = mkOption {
+ type = types.listOf types.str;
+ default = [];
+ example = ["www.example.org" "www.example.org:8080" "example.org"];
+ description = ''
+ Additional names of virtual hosts served by this virtual host configuration.
+ '';
+ };
+
+ listen = mkOption {
+ type = types.listOf (types.submodule (
+ {
+ options = {
+ port = mkOption {
+ type = types.int;
+ description = "port to listen on";
+ };
+ ip = mkOption {
+ type = types.str;
+ default = "*";
+ description = "Ip to listen on. 0.0.0.0 for ipv4 only, * for all.";
+ };
+ };
+ } ));
+ description = ''
+ List of { /* ip: "*"; */ port = 80;} to listen on
+ '';
+
+ default = [];
+ };
+
+ enableSSL = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Whether to enable SSL (https) support.";
+ };
+
+ # Note: sslServerCert and sslServerKey can be left empty, but this
+ # only makes sense for virtual hosts (they will inherit from the
+ # main server).
+
+ sslServerCert = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ example = "/var/host.cert";
+ description = "Path to server SSL certificate.";
+ };
+
+ sslServerKey = mkOption {
+ type = types.path;
+ example = "/var/host.key";
+ description = "Path to server SSL certificate key.";
+ };
+
+ sslServerChain = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ example = "/var/ca.pem";
+ description = "Path to server SSL chain file.";
+ };
+
+ adminAddr = mkOption ({
+ type = types.nullOr types.str;
+ example = "admin@example.org";
+ description = "E-mail address of the server administrator.";
+ } // (if forMainServer then {} else {default = null;}));
+
+ documentRoot = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ example = "/data/webserver/docs";
+ description = ''
+ The path of Apache's document root directory. If left undefined,
+ an empty directory in the Nix store will be used as root.
+ '';
+ };
+
+ servedDirs = mkOption {
+ type = types.listOf types.attrs;
+ default = [];
+ example = [
+ { urlPath = "/nix";
+ dir = "/home/eelco/Dev/nix-homepage";
+ }
+ ];
+ description = ''
+ This option provides a simple way to serve static directories.
+ '';
+ };
+
+ servedFiles = mkOption {
+ type = types.listOf types.attrs;
+ default = [];
+ example = [
+ { urlPath = "/foo/bar.png";
+ file = "/home/eelco/some-file.png";
+ }
+ ];
+ description = ''
+ This option provides a simple way to serve individual, static files.
+ '';
+ };
+
+ extraConfig = mkOption {
+ type = types.lines;
+ default = "";
+ example = ''
+ <Directory /home>
+ Options FollowSymlinks
+ AllowOverride All
+ </Directory>
+ '';
+ description = ''
+ These lines go to httpd.conf verbatim. They will go after
+ directories and directory aliases defined by default.
+ '';
+ };
+
+ extraSubservices = mkOption {
+ type = types.listOf types.unspecified;
+ default = [];
+ description = "Extra subservices to enable in the webserver.";
+ };
+
+ enableUserDir = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Whether to enable serving <filename>~/public_html</filename> as
+ <literal>/~<replaceable>username</replaceable></literal>.
+ '';
+ };
+
+ globalRedirect = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ example = http://newserver.example.org/;
+ description = ''
+ If set, all requests for this host are redirected permanently to
+ the given URL.
+ '';
+ };
+
+ logFormat = mkOption {
+ type = types.str;
+ default = "common";
+ example = "combined";
+ description = ''
+ Log format for Apache's log files. Possible values are: combined, common, referer, agent.
+ '';
+ };
+
+ robotsEntries = mkOption {
+ type = types.lines;
+ default = "";
+ example = "Disallow: /foo/";
+ description = ''
+ Specification of pages to be ignored by web crawlers. See <link
+ xlink:href='http://www.robotstxt.org/'/> for details.
+ '';
+ };
+
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/caddy.nix b/nixpkgs/nixos/modules/services/web-servers/caddy.nix
new file mode 100644
index 00000000000..132c50735d9
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/caddy.nix
@@ -0,0 +1,105 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.services.caddy;
+ configFile = pkgs.writeText "Caddyfile" cfg.config;
+in {
+ options.services.caddy = {
+ enable = mkEnableOption "Caddy web server";
+
+ config = mkOption {
+ default = "";
+ example = ''
+ example.com {
+ gzip
+ minify
+ log syslog
+
+ root /srv/http
+ }
+ '';
+ type = types.lines;
+ description = "Verbatim Caddyfile to use";
+ };
+
+ ca = mkOption {
+ default = "https://acme-v02.api.letsencrypt.org/directory";
+ example = "https://acme-staging-v02.api.letsencrypt.org/directory";
+ type = types.str;
+ description = "Certificate authority ACME server. The default (Let's Encrypt production server) should be fine for most people.";
+ };
+
+ email = mkOption {
+ default = "";
+ type = types.str;
+ description = "Email address (for Let's Encrypt certificate)";
+ };
+
+ agree = mkOption {
+ default = false;
+ type = types.bool;
+ description = "Agree to Let's Encrypt Subscriber Agreement";
+ };
+
+ dataDir = mkOption {
+ default = "/var/lib/caddy";
+ type = types.path;
+ description = ''
+ The data directory, for storing certificates. Before 17.09, this
+ would create a .caddy directory. With 17.09 the contents of the
+ .caddy directory are in the specified data directory instead.
+ '';
+ };
+
+ package = mkOption {
+ default = pkgs.caddy;
+ defaultText = "pkgs.caddy";
+ type = types.package;
+ description = "Caddy package to use.";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ systemd.services.caddy = {
+ description = "Caddy web server";
+ after = [ "network-online.target" ];
+ wantedBy = [ "multi-user.target" ];
+ environment = mkIf (versionAtLeast config.system.stateVersion "17.09")
+ { CADDYPATH = cfg.dataDir; };
+ serviceConfig = {
+ ExecStart = ''
+ ${cfg.package}/bin/caddy -root=/var/tmp -conf=${configFile} \
+ -ca=${cfg.ca} -email=${cfg.email} ${optionalString cfg.agree "-agree"}
+ '';
+ ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+ Type = "simple";
+ User = "caddy";
+ Group = "caddy";
+ Restart = "on-failure";
+ StartLimitInterval = 86400;
+ StartLimitBurst = 5;
+ AmbientCapabilities = "cap_net_bind_service";
+ CapabilityBoundingSet = "cap_net_bind_service";
+ NoNewPrivileges = true;
+ LimitNPROC = 64;
+ LimitNOFILE = 1048576;
+ PrivateTmp = true;
+ PrivateDevices = true;
+ ProtectHome = true;
+ ProtectSystem = "full";
+ ReadWriteDirectories = cfg.dataDir;
+ };
+ };
+
+ users.users.caddy = {
+ group = "caddy";
+ uid = config.ids.uids.caddy;
+ home = cfg.dataDir;
+ createHome = true;
+ };
+
+ users.groups.caddy.gid = config.ids.uids.caddy;
+ };
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/darkhttpd.nix b/nixpkgs/nixos/modules/services/web-servers/darkhttpd.nix
new file mode 100644
index 00000000000..d6649fd472d
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/darkhttpd.nix
@@ -0,0 +1,77 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.services.darkhttpd;
+
+ args = concatStringsSep " " ([
+ cfg.rootDir
+ "--port ${toString cfg.port}"
+ "--addr ${cfg.address}"
+ ] ++ cfg.extraArgs
+ ++ optional cfg.hideServerId "--no-server-id"
+ ++ optional config.networking.enableIPv6 "--ipv6");
+
+in {
+ options.services.darkhttpd = with types; {
+ enable = mkEnableOption "DarkHTTPd web server";
+
+ port = mkOption {
+ default = 80;
+ type = ints.u16;
+ description = ''
+ Port to listen on.
+ Pass 0 to let the system choose any free port for you.
+ '';
+ };
+
+ address = mkOption {
+ default = "127.0.0.1";
+ type = str;
+ description = ''
+ Address to listen on.
+ Pass `all` to listen on all interfaces.
+ '';
+ };
+
+ rootDir = mkOption {
+ type = path;
+ description = ''
+ Path from which to serve files.
+ '';
+ };
+
+ hideServerId = mkOption {
+ type = bool;
+ default = true;
+ description = ''
+ Don't identify the server type in headers or directory listings.
+ '';
+ };
+
+ extraArgs = mkOption {
+ type = listOf str;
+ default = [];
+ description = ''
+ Additional configuration passed to the executable.
+ '';
+ };
+ };
+
+ config = mkIf cfg.enable {
+ systemd.services.darkhttpd = {
+ description = "Dark HTTPd";
+ wants = [ "network.target" ];
+ after = [ "network.target" ];
+ wantedBy = [ "multi-user.target" ];
+ serviceConfig = {
+ DynamicUser = true;
+ ExecStart = "${pkgs.darkhttpd}/bin/darkhttpd ${args}";
+ AmbientCapabilities = lib.mkIf (cfg.port < 1024) [ "CAP_NET_BIND_SERVICE" ];
+ Restart = "on-failure";
+ RestartSec = "2s";
+ };
+ };
+ };
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/fcgiwrap.nix b/nixpkgs/nixos/modules/services/web-servers/fcgiwrap.nix
new file mode 100644
index 00000000000..a64a187255a
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/fcgiwrap.nix
@@ -0,0 +1,72 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.services.fcgiwrap;
+in {
+
+ options = {
+ services.fcgiwrap = {
+ enable = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Whether to enable fcgiwrap, a server for running CGI applications over FastCGI.";
+ };
+
+ preforkProcesses = mkOption {
+ type = types.int;
+ default = 1;
+ description = "Number of processes to prefork.";
+ };
+
+ socketType = mkOption {
+ type = types.enum [ "unix" "tcp" "tcp6" ];
+ default = "unix";
+ description = "Socket type: 'unix', 'tcp' or 'tcp6'.";
+ };
+
+ socketAddress = mkOption {
+ type = types.str;
+ default = "/run/fcgiwrap.sock";
+ example = "1.2.3.4:5678";
+ description = "Socket address. In case of a UNIX socket, this should be its filesystem path.";
+ };
+
+ user = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = "User permissions for the socket.";
+ };
+
+ group = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = "Group permissions for the socket.";
+ };
+ };
+ };
+
+ config = mkIf cfg.enable {
+ systemd.services.fcgiwrap = {
+ after = [ "nss-user-lookup.target" ];
+ wantedBy = optional (cfg.socketType != "unix") "multi-user.target";
+
+ serviceConfig = {
+ ExecStart = "${pkgs.fcgiwrap}/sbin/fcgiwrap -c ${builtins.toString cfg.preforkProcesses} ${
+ if (cfg.socketType != "unix") then "-s ${cfg.socketType}:${cfg.socketAddress}" else ""
+ }";
+ } // (if cfg.user != null && cfg.group != null then {
+ User = cfg.user;
+ Group = cfg.group;
+ } else { } );
+ };
+
+ systemd.sockets = if (cfg.socketType == "unix") then {
+ fcgiwrap = {
+ wantedBy = [ "sockets.target" ];
+ socketConfig.ListenStream = cfg.socketAddress;
+ };
+ } else { };
+ };
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/hitch/default.nix b/nixpkgs/nixos/modules/services/web-servers/hitch/default.nix
new file mode 100644
index 00000000000..a6c4cbea122
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/hitch/default.nix
@@ -0,0 +1,108 @@
+{ config, lib, pkgs, ...}:
+let
+ cfg = config.services.hitch;
+ ocspDir = lib.optionalString cfg.ocsp-stapling.enabled "/var/cache/hitch/ocsp";
+ hitchConfig = with lib; pkgs.writeText "hitch.conf" (concatStringsSep "\n" [
+ ("backend = \"${cfg.backend}\"")
+ (concatMapStrings (s: "frontend = \"${s}\"\n") cfg.frontend)
+ (concatMapStrings (s: "pem-file = \"${s}\"\n") cfg.pem-files)
+ ("ciphers = \"${cfg.ciphers}\"")
+ ("ocsp-dir = \"${ocspDir}\"")
+ "user = \"${cfg.user}\""
+ "group = \"${cfg.group}\""
+ cfg.extraConfig
+ ]);
+in
+with lib;
+{
+ options = {
+ services.hitch = {
+ enable = mkEnableOption "Hitch Server";
+
+ backend = mkOption {
+ type = types.str;
+ description = ''
+ The host and port Hitch connects to when receiving
+ a connection in the form [HOST]:PORT
+ '';
+ };
+
+ ciphers = mkOption {
+ type = types.str;
+ default = "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
+ description = "The list of ciphers to use";
+ };
+
+ frontend = mkOption {
+ type = types.either types.str (types.listOf types.str);
+ default = "[127.0.0.1]:443";
+ description = ''
+ The port and interface of the listen endpoint in the
++ form [HOST]:PORT[+CERT].
+ '';
+ apply = toList;
+ };
+
+ pem-files = mkOption {
+ type = types.listOf types.path;
+ default = [];
+ description = "PEM files to use";
+ };
+
+ ocsp-stapling = {
+ enabled = mkOption {
+ type = types.bool;
+ default = true;
+ description = "Whether to enable OCSP Stapling";
+ };
+ };
+
+ user = mkOption {
+ type = types.str;
+ default = "hitch";
+ description = "The user to run as";
+ };
+
+ group = mkOption {
+ type = types.str;
+ default = "hitch";
+ description = "The group to run as";
+ };
+
+ extraConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = "Additional configuration lines";
+ };
+ };
+
+ };
+
+ config = mkIf cfg.enable {
+
+ systemd.services.hitch = {
+ description = "Hitch";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.target" ];
+ preStart = ''
+ ${pkgs.hitch}/sbin/hitch -t --config ${hitchConfig}
+ '' + (optionalString cfg.ocsp-stapling.enabled ''
+ mkdir -p ${ocspDir}
+ chown -R hitch:hitch ${ocspDir}
+ '');
+ serviceConfig = {
+ Type = "forking";
+ ExecStart = "${pkgs.hitch}/sbin/hitch --daemon --config ${hitchConfig}";
+ ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+ Restart = "always";
+ RestartSec = "5s";
+ LimitNOFILE = 131072;
+ };
+ };
+
+ environment.systemPackages = [ pkgs.hitch ];
+
+ users.users.hitch.group = "hitch";
+ users.groups.hitch = {};
+ };
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/hydron.nix b/nixpkgs/nixos/modules/services/web-servers/hydron.nix
new file mode 100644
index 00000000000..a4a5a435b2e
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/hydron.nix
@@ -0,0 +1,165 @@
+{ config, lib, pkgs, ... }:
+
+let
+ cfg = config.services.hydron;
+in with lib; {
+ options.services.hydron = {
+ enable = mkEnableOption "hydron";
+
+ dataDir = mkOption {
+ type = types.path;
+ default = "/var/lib/hydron";
+ example = "/home/okina/hydron";
+ description = "Location where hydron runs and stores data.";
+ };
+
+ interval = mkOption {
+ type = types.str;
+ default = "weekly";
+ example = "06:00";
+ description = ''
+ How often we run hydron import and possibly fetch tags. Runs by default every week.
+
+ The format is described in
+ <citerefentry><refentrytitle>systemd.time</refentrytitle>
+ <manvolnum>7</manvolnum></citerefentry>.
+ '';
+ };
+
+ password = mkOption {
+ type = types.str;
+ default = "hydron";
+ example = "dumbpass";
+ description = "Password for the hydron database.";
+ };
+
+ passwordFile = mkOption {
+ type = types.path;
+ default = "/run/keys/hydron-password-file";
+ example = "/home/okina/hydron/keys/pass";
+ description = "Password file for the hydron database.";
+ };
+
+ postgresArgs = mkOption {
+ type = types.str;
+ description = "Postgresql connection arguments.";
+ example = ''
+ {
+ "driver": "postgres",
+ "connection": "user=hydron password=dumbpass dbname=hydron sslmode=disable"
+ }
+ '';
+ };
+
+ postgresArgsFile = mkOption {
+ type = types.path;
+ default = "/run/keys/hydron-postgres-args";
+ example = "/home/okina/hydron/keys/postgres";
+ description = "Postgresql connection arguments file.";
+ };
+
+ listenAddress = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ example = "127.0.0.1:8010";
+ description = "Listen on a specific IP address and port.";
+ };
+
+ importPaths = mkOption {
+ type = types.listOf types.path;
+ default = [];
+ example = [ "/home/okina/Pictures" ];
+ description = "Paths that hydron will recursively import.";
+ };
+
+ fetchTags = mkOption {
+ type = types.bool;
+ default = true;
+ description = "Fetch tags for imported images and webm from gelbooru.";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ services.hydron.passwordFile = mkDefault (pkgs.writeText "hydron-password-file" cfg.password);
+ services.hydron.postgresArgsFile = mkDefault (pkgs.writeText "hydron-postgres-args" cfg.postgresArgs);
+ services.hydron.postgresArgs = mkDefault ''
+ {
+ "driver": "postgres",
+ "connection": "user=hydron password=${cfg.password} host=/run/postgresql dbname=hydron sslmode=disable"
+ }
+ '';
+
+ services.postgresql = {
+ enable = true;
+ ensureDatabases = [ "hydron" ];
+ ensureUsers = [
+ { name = "hydron";
+ ensurePermissions = { "DATABASE hydron" = "ALL PRIVILEGES"; };
+ }
+ ];
+ };
+
+ systemd.tmpfiles.rules = [
+ "d '${cfg.dataDir}' 0750 hydron hydron - -"
+ "d '${cfg.dataDir}/.hydron' - hydron hydron - -"
+ "d '${cfg.dataDir}/images' - hydron hydron - -"
+ "Z '${cfg.dataDir}' - hydron hydron - -"
+
+ "L+ '${cfg.dataDir}/.hydron/db_conf.json' - - - - ${cfg.postgresArgsFile}"
+ ];
+
+ systemd.services.hydron = {
+ description = "hydron";
+ after = [ "network.target" "postgresql.service" ];
+ wantedBy = [ "multi-user.target" ];
+
+ serviceConfig = {
+ User = "hydron";
+ Group = "hydron";
+ ExecStart = "${pkgs.hydron}/bin/hydron serve"
+ + optionalString (cfg.listenAddress != null) " -a ${cfg.listenAddress}";
+ };
+ };
+
+ systemd.services.hydron-fetch = {
+ description = "Import paths into hydron and possibly fetch tags";
+
+ serviceConfig = {
+ Type = "oneshot";
+ User = "hydron";
+ Group = "hydron";
+ ExecStart = "${pkgs.hydron}/bin/hydron import "
+ + optionalString cfg.fetchTags "-f "
+ + (escapeShellArg cfg.dataDir) + "/images " + (escapeShellArgs cfg.importPaths);
+ };
+ };
+
+ systemd.timers.hydron-fetch = {
+ description = "Automatically import paths into hydron and possibly fetch tags";
+ after = [ "network.target" "hydron.service" ];
+ wantedBy = [ "timers.target" ];
+
+ timerConfig = {
+ Persistent = true;
+ OnCalendar = cfg.interval;
+ };
+ };
+
+ users = {
+ groups.hydron.gid = config.ids.gids.hydron;
+
+ users.hydron = {
+ description = "hydron server service user";
+ home = cfg.dataDir;
+ group = "hydron";
+ uid = config.ids.uids.hydron;
+ };
+ };
+ };
+
+ imports = [
+ (mkRenamedOptionModule [ "services" "hydron" "baseDir" ] [ "services" "hydron" "dataDir" ])
+ ];
+
+ meta.maintainers = with maintainers; [ chiiruno ];
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/jboss/builder.sh b/nixpkgs/nixos/modules/services/web-servers/jboss/builder.sh
new file mode 100644
index 00000000000..2eb89a90f67
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/jboss/builder.sh
@@ -0,0 +1,72 @@
+set -e
+
+source $stdenv/setup
+
+mkdir -p $out/bin
+
+cat > $out/bin/control <<EOF
+mkdir -p $logDir
+chown -R $user $logDir
+export PATH=$PATH:$su/bin
+
+start()
+{
+ su $user -s /bin/sh -c "$jboss/bin/run.sh \
+ -Djboss.server.base.dir=$serverDir \
+ -Djboss.server.base.url=file://$serverDir \
+ -Djboss.server.temp.dir=$tempDir \
+ -Djboss.server.log.dir=$logDir \
+ -Djboss.server.lib.url=$libUrl \
+ -c default"
+}
+
+stop()
+{
+ su $user -s /bin/sh -c "$jboss/bin/shutdown.sh -S"
+}
+
+if test "\$1" = start
+then
+ trap stop 15
+
+ start
+elif test "\$1" = stop
+then
+ stop
+elif test "\$1" = init
+then
+ echo "Are you sure you want to create a new server instance (old server instance will be lost!)?"
+ read answer
+
+ if ! test \$answer = "yes"
+ then
+ exit 1
+ fi
+
+ rm -rf $serverDir
+ mkdir -p $serverDir
+ cd $serverDir
+ cp -av $jboss/server/default .
+ sed -i -e "s|deploy/|$deployDir|" default/conf/jboss-service.xml
+
+ if ! test "$useJK" = ""
+ then
+ sed -i -e 's|<attribute name="UseJK">false</attribute>|<attribute name="UseJK">true</attribute>|' default/deploy/jboss-web.deployer/META-INF/jboss-service.xml
+ sed -i -e 's|<Engine name="jboss.web" defaultHost="localhost">|<Engine name="jboss.web" defaultHost="localhost" jvmRoute="node1">|' default/deploy/jboss-web.deployer/server.xml
+ fi
+
+ # Make files accessible for the server user
+
+ chown -R $user $serverDir
+ for i in \`find $serverDir -type d\`
+ do
+ chmod 755 \$i
+ done
+ for i in \`find $serverDir -type f\`
+ do
+ chmod 644 \$i
+ done
+fi
+EOF
+
+chmod +x $out/bin/*
diff --git a/nixpkgs/nixos/modules/services/web-servers/jboss/default.nix b/nixpkgs/nixos/modules/services/web-servers/jboss/default.nix
new file mode 100644
index 00000000000..d28724281a8
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/jboss/default.nix
@@ -0,0 +1,80 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.services.jboss;
+
+ jbossService = pkgs.stdenv.mkDerivation {
+ name = "jboss-server";
+ builder = ./builder.sh;
+ inherit (pkgs) jboss su;
+ inherit (cfg) tempDir logDir libUrl deployDir serverDir user useJK;
+ };
+
+in
+
+{
+
+ ###### interface
+
+ options = {
+
+ services.jboss = {
+
+ enable = mkOption {
+ default = false;
+ description = "Whether to enable JBoss. WARNING : this package is outdated and is known to have vulnerabilities.";
+ };
+
+ tempDir = mkOption {
+ default = "/tmp";
+ description = "Location where JBoss stores its temp files";
+ };
+
+ logDir = mkOption {
+ default = "/var/log/jboss";
+ description = "Location of the logfile directory of JBoss";
+ };
+
+ serverDir = mkOption {
+ description = "Location of the server instance files";
+ default = "/var/jboss/server";
+ };
+
+ deployDir = mkOption {
+ description = "Location of the deployment files";
+ default = "/nix/var/nix/profiles/default/server/default/deploy/";
+ };
+
+ libUrl = mkOption {
+ default = "file:///nix/var/nix/profiles/default/server/default/lib";
+ description = "Location where the shared library JARs are stored";
+ };
+
+ user = mkOption {
+ default = "nobody";
+ description = "User account under which jboss runs.";
+ };
+
+ useJK = mkOption {
+ default = false;
+ description = "Whether to use to connector to the Apache HTTP server";
+ };
+
+ };
+
+ };
+
+
+ ###### implementation
+
+ config = mkIf config.services.jboss.enable {
+ systemd.services.jboss = {
+ description = "JBoss server";
+ script = "${jbossService}/bin/control start";
+ wantedBy = [ "multi-user.target" ];
+ };
+ };
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/lighttpd/cgit.nix b/nixpkgs/nixos/modules/services/web-servers/lighttpd/cgit.nix
new file mode 100644
index 00000000000..9f25dc34f3f
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/lighttpd/cgit.nix
@@ -0,0 +1,91 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.services.lighttpd.cgit;
+ pathPrefix = if stringLength cfg.subdir == 0 then "" else "/" + cfg.subdir;
+ configFile = pkgs.writeText "cgitrc"
+ ''
+ # default paths to static assets
+ css=${pathPrefix}/cgit.css
+ logo=${pathPrefix}/cgit.png
+ favicon=${pathPrefix}/favicon.ico
+
+ # user configuration
+ ${cfg.configText}
+ '';
+in
+{
+
+ options.services.lighttpd.cgit = {
+
+ enable = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ If true, enable cgit (fast web interface for git repositories) as a
+ sub-service in lighttpd.
+ '';
+ };
+
+ subdir = mkOption {
+ default = "cgit";
+ example = "";
+ type = types.str;
+ description = ''
+ The subdirectory in which to serve cgit. The web application will be
+ accessible at http://yourserver/''${subdir}
+ '';
+ };
+
+ configText = mkOption {
+ default = "";
+ example = ''
+ source-filter=''${pkgs.cgit}/lib/cgit/filters/syntax-highlighting.py
+ about-filter=''${pkgs.cgit}/lib/cgit/filters/about-formatting.sh
+ cache-size=1000
+ scan-path=/srv/git
+ '';
+ type = types.lines;
+ description = ''
+ Verbatim contents of the cgit runtime configuration file. Documentation
+ (with cgitrc example file) is available in "man cgitrc". Or online:
+ http://git.zx2c4.com/cgit/tree/cgitrc.5.txt
+ '';
+ };
+
+ };
+
+ config = mkIf cfg.enable {
+
+ # make the cgitrc manpage available
+ environment.systemPackages = [ pkgs.cgit ];
+
+ # declare module dependencies
+ services.lighttpd.enableModules = [ "mod_cgi" "mod_alias" "mod_setenv" ];
+
+ services.lighttpd.extraConfig = ''
+ $HTTP["url"] =~ "^/${cfg.subdir}" {
+ cgi.assign = (
+ "cgit.cgi" => "${pkgs.cgit}/cgit/cgit.cgi"
+ )
+ alias.url = (
+ "${pathPrefix}/cgit.css" => "${pkgs.cgit}/cgit/cgit.css",
+ "${pathPrefix}/cgit.png" => "${pkgs.cgit}/cgit/cgit.png",
+ "${pathPrefix}" => "${pkgs.cgit}/cgit/cgit.cgi"
+ )
+ setenv.add-environment = (
+ "CGIT_CONFIG" => "${configFile}"
+ )
+ }
+ '';
+
+ systemd.services.lighttpd.preStart = ''
+ mkdir -p /var/cache/cgit
+ chown lighttpd:lighttpd /var/cache/cgit
+ '';
+
+ };
+
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/lighttpd/collectd.nix b/nixpkgs/nixos/modules/services/web-servers/lighttpd/collectd.nix
new file mode 100644
index 00000000000..3f262451c2c
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/lighttpd/collectd.nix
@@ -0,0 +1,58 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.services.lighttpd.collectd;
+
+ collectionConf = pkgs.writeText "collection.conf" ''
+ datadir: "${config.services.collectd.dataDir}"
+ libdir: "${config.services.collectd.package}/lib/collectd"
+ '';
+
+ defaultCollectionCgi = config.services.collectd.package.overrideDerivation(old: {
+ name = "collection.cgi";
+ dontConfigure = true;
+ buildPhase = "true";
+ installPhase = ''
+ substituteInPlace contrib/collection.cgi --replace '"/etc/collection.conf"' '$ENV{COLLECTION_CONF}'
+ cp contrib/collection.cgi $out
+ '';
+ });
+in
+{
+
+ options.services.lighttpd.collectd = {
+
+ enable = mkEnableOption "collectd subservice accessible at http://yourserver/collectd";
+
+ collectionCgi = mkOption {
+ type = types.path;
+ default = defaultCollectionCgi;
+ description = ''
+ Path to collection.cgi script from (collectd sources)/contrib/collection.cgi
+ This option allows to use a customized version
+ '';
+ };
+ };
+
+ config = mkIf cfg.enable {
+ services.lighttpd.enableModules = [ "mod_cgi" "mod_alias" "mod_setenv" ];
+
+ services.lighttpd.extraConfig = ''
+ $HTTP["url"] =~ "^/collectd" {
+ cgi.assign = (
+ ".cgi" => "${pkgs.perl}/bin/perl"
+ )
+ alias.url = (
+ "/collectd" => "${cfg.collectionCgi}"
+ )
+ setenv.add-environment = (
+ "PERL5LIB" => "${with pkgs.perlPackages; makePerlPath [ CGI HTMLParser URI pkgs.rrdtool ]}",
+ "COLLECTION_CONF" => "${collectionConf}"
+ )
+ }
+ '';
+ };
+
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/lighttpd/default.nix b/nixpkgs/nixos/modules/services/web-servers/lighttpd/default.nix
new file mode 100644
index 00000000000..7a3df26e47a
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/lighttpd/default.nix
@@ -0,0 +1,256 @@
+# NixOS module for lighttpd web server
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.services.lighttpd;
+
+ # List of known lighttpd modules, ordered by how the lighttpd documentation
+ # recommends them being imported:
+ # http://redmine.lighttpd.net/projects/1/wiki/Server_modulesDetails
+ #
+ # Some modules are always imported and should not appear in the config:
+ # disallowedModules = [ "mod_indexfile" "mod_dirlisting" "mod_staticfile" ];
+ #
+ # For full module list, see the output of running ./configure in the lighttpd
+ # source.
+ allKnownModules = [
+ "mod_rewrite"
+ "mod_redirect"
+ "mod_alias"
+ "mod_access"
+ "mod_auth"
+ "mod_status"
+ "mod_simple_vhost"
+ "mod_evhost"
+ "mod_userdir"
+ "mod_secdownload"
+ "mod_fastcgi"
+ "mod_proxy"
+ "mod_cgi"
+ "mod_ssi"
+ "mod_compress"
+ "mod_usertrack"
+ "mod_expire"
+ "mod_rrdtool"
+ "mod_accesslog"
+ # Remaining list of modules, order assumed to be unimportant.
+ "mod_authn_file"
+ "mod_authn_gssapi"
+ "mod_authn_ldap"
+ "mod_authn_mysql"
+ "mod_cml"
+ "mod_deflate"
+ "mod_evasive"
+ "mod_extforward"
+ "mod_flv_streaming"
+ "mod_geoip"
+ "mod_magnet"
+ "mod_mysql_vhost"
+ "mod_openssl" # since v1.4.46
+ "mod_scgi"
+ "mod_setenv"
+ "mod_trigger_b4_dl"
+ "mod_uploadprogress"
+ "mod_vhostdb" # since v1.4.46
+ "mod_webdav"
+ "mod_wstunnel" # since v1.4.46
+ ];
+
+ maybeModuleString = moduleName:
+ if elem moduleName cfg.enableModules then ''"${moduleName}"'' else "";
+
+ modulesIncludeString = concatStringsSep ",\n"
+ (filter (x: x != "") (map maybeModuleString allKnownModules));
+
+ configFile = if cfg.configText != "" then
+ pkgs.writeText "lighttpd.conf" ''
+ ${cfg.configText}
+ ''
+ else
+ pkgs.writeText "lighttpd.conf" ''
+ server.document-root = "${cfg.document-root}"
+ server.port = ${toString cfg.port}
+ server.username = "lighttpd"
+ server.groupname = "lighttpd"
+
+ # As for why all modules are loaded here, instead of having small
+ # server.modules += () entries in each sub-service extraConfig snippet,
+ # read this:
+ #
+ # http://redmine.lighttpd.net/projects/1/wiki/Server_modulesDetails
+ # http://redmine.lighttpd.net/issues/2337
+ #
+ # Basically, lighttpd doesn't want to load (or even silently ignore) a
+ # module for a second time, and there is no way to check if a module has
+ # been loaded already. So if two services were to put the same module in
+ # server.modules += (), that would break the lighttpd configuration.
+ server.modules = (
+ ${modulesIncludeString}
+ )
+
+ # Logging (logs end up in systemd journal)
+ accesslog.use-syslog = "enable"
+ server.errorlog-use-syslog = "enable"
+
+ ${lib.optionalString cfg.enableUpstreamMimeTypes ''
+ include "${pkgs.lighttpd}/share/lighttpd/doc/config/conf.d/mime.conf"
+ ''}
+
+ static-file.exclude-extensions = ( ".fcgi", ".php", ".rb", "~", ".inc" )
+ index-file.names = ( "index.html" )
+
+ ${if cfg.mod_userdir then ''
+ userdir.path = "public_html"
+ '' else ""}
+
+ ${if cfg.mod_status then ''
+ status.status-url = "/server-status"
+ status.statistics-url = "/server-statistics"
+ status.config-url = "/server-config"
+ '' else ""}
+
+ ${cfg.extraConfig}
+ '';
+
+in
+
+{
+
+ options = {
+
+ services.lighttpd = {
+
+ enable = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Enable the lighttpd web server.
+ '';
+ };
+
+ port = mkOption {
+ default = 80;
+ type = types.int;
+ description = ''
+ TCP port number for lighttpd to bind to.
+ '';
+ };
+
+ document-root = mkOption {
+ default = "/srv/www";
+ type = types.path;
+ description = ''
+ Document-root of the web server. Must be readable by the "lighttpd" user.
+ '';
+ };
+
+ mod_userdir = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ If true, requests in the form /~user/page.html are rewritten to take
+ the file public_html/page.html from the home directory of the user.
+ '';
+ };
+
+ enableModules = mkOption {
+ type = types.listOf types.str;
+ default = [ ];
+ example = [ "mod_cgi" "mod_status" ];
+ description = ''
+ List of lighttpd modules to enable. Sub-services take care of
+ enabling modules as needed, so this option is mainly for when you
+ want to add custom stuff to
+ <option>services.lighttpd.extraConfig</option> that depends on a
+ certain module.
+ '';
+ };
+
+ enableUpstreamMimeTypes = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ Whether to include the list of mime types bundled with lighttpd
+ (upstream). If you disable this, no mime types will be added by
+ NixOS and you will have to add your own mime types in
+ <option>services.lighttpd.extraConfig</option>.
+ '';
+ };
+
+ mod_status = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Show server status overview at /server-status, statistics at
+ /server-statistics and list of loaded modules at /server-config.
+ '';
+ };
+
+ configText = mkOption {
+ default = "";
+ type = types.lines;
+ example = ''...verbatim config file contents...'';
+ description = ''
+ Overridable config file contents to use for lighttpd. By default, use
+ the contents automatically generated by NixOS.
+ '';
+ };
+
+ extraConfig = mkOption {
+ default = "";
+ type = types.lines;
+ description = ''
+ These configuration lines will be appended to the generated lighttpd
+ config file. Note that this mechanism does not work when the manual
+ <option>configText</option> option is used.
+ '';
+ };
+
+ };
+
+ };
+
+ config = mkIf cfg.enable {
+
+ assertions = [
+ { assertion = all (x: elem x allKnownModules) cfg.enableModules;
+ message = ''
+ One (or more) modules in services.lighttpd.enableModules are
+ unrecognized.
+
+ Known modules: ${toString allKnownModules}
+
+ services.lighttpd.enableModules: ${toString cfg.enableModules}
+ '';
+ }
+ ];
+
+ services.lighttpd.enableModules = mkMerge
+ [ (mkIf cfg.mod_status [ "mod_status" ])
+ (mkIf cfg.mod_userdir [ "mod_userdir" ])
+ # always load mod_accesslog so that we can log to the journal
+ [ "mod_accesslog" ]
+ ];
+
+ systemd.services.lighttpd = {
+ description = "Lighttpd Web Server";
+ after = [ "network.target" ];
+ wantedBy = [ "multi-user.target" ];
+ serviceConfig.ExecStart = "${pkgs.lighttpd}/sbin/lighttpd -D -f ${configFile}";
+ # SIGINT => graceful shutdown
+ serviceConfig.KillSignal = "SIGINT";
+ };
+
+ users.users.lighttpd = {
+ group = "lighttpd";
+ description = "lighttpd web server privilege separation user";
+ uid = config.ids.uids.lighttpd;
+ };
+
+ users.groups.lighttpd.gid = config.ids.gids.lighttpd;
+ };
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/lighttpd/gitweb.nix b/nixpkgs/nixos/modules/services/web-servers/lighttpd/gitweb.nix
new file mode 100644
index 00000000000..c494d6966a7
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/lighttpd/gitweb.nix
@@ -0,0 +1,52 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.services.gitweb;
+ package = pkgs.gitweb.override (optionalAttrs cfg.gitwebTheme {
+ gitwebTheme = true;
+ });
+
+in
+{
+
+ options.services.lighttpd.gitweb = {
+
+ enable = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ If true, enable gitweb in lighttpd. Access it at http://yourserver/gitweb
+ '';
+ };
+
+ };
+
+ config = mkIf config.services.lighttpd.gitweb.enable {
+
+ # declare module dependencies
+ services.lighttpd.enableModules = [ "mod_cgi" "mod_redirect" "mod_alias" "mod_setenv" ];
+
+ services.lighttpd.extraConfig = ''
+ $HTTP["url"] =~ "^/gitweb" {
+ cgi.assign = (
+ ".cgi" => "${pkgs.perl}/bin/perl"
+ )
+ url.redirect = (
+ "^/gitweb$" => "/gitweb/"
+ )
+ alias.url = (
+ "/gitweb/static/" => "${package}/static/",
+ "/gitweb/" => "${package}/gitweb.cgi"
+ )
+ setenv.add-environment = (
+ "GITWEB_CONFIG" => "${cfg.gitwebConfigFile}",
+ "HOME" => "${cfg.projectroot}"
+ )
+ }
+ '';
+
+ };
+
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/meguca.nix b/nixpkgs/nixos/modules/services/web-servers/meguca.nix
new file mode 100644
index 00000000000..5a00070dc94
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/meguca.nix
@@ -0,0 +1,174 @@
+{ config, lib, pkgs, ... }:
+
+let
+ cfg = config.services.meguca;
+ postgres = config.services.postgresql;
+in with lib; {
+ options.services.meguca = {
+ enable = mkEnableOption "meguca";
+
+ dataDir = mkOption {
+ type = types.path;
+ default = "/var/lib/meguca";
+ example = "/home/okina/meguca";
+ description = "Location where meguca stores it's database and links.";
+ };
+
+ password = mkOption {
+ type = types.str;
+ default = "meguca";
+ example = "dumbpass";
+ description = "Password for the meguca database.";
+ };
+
+ passwordFile = mkOption {
+ type = types.path;
+ default = "/run/keys/meguca-password-file";
+ example = "/home/okina/meguca/keys/pass";
+ description = "Password file for the meguca database.";
+ };
+
+ reverseProxy = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ example = "192.168.1.5";
+ description = "Reverse proxy IP.";
+ };
+
+ sslCertificate = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ example = "/home/okina/meguca/ssl.cert";
+ description = "Path to the SSL certificate.";
+ };
+
+ listenAddress = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ example = "127.0.0.1:8000";
+ description = "Listen on a specific IP address and port.";
+ };
+
+ cacheSize = mkOption {
+ type = types.nullOr types.int;
+ default = null;
+ example = 256;
+ description = "Cache size in MB.";
+ };
+
+ postgresArgs = mkOption {
+ type = types.str;
+ example = "user=meguca password=dumbpass dbname=meguca sslmode=disable";
+ description = "Postgresql connection arguments.";
+ };
+
+ postgresArgsFile = mkOption {
+ type = types.path;
+ default = "/run/keys/meguca-postgres-args";
+ example = "/home/okina/meguca/keys/postgres";
+ description = "Postgresql connection arguments file.";
+ };
+
+ compressTraffic = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Compress all traffic with gzip.";
+ };
+
+ assumeReverseProxy = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Assume the server is behind a reverse proxy, when resolving client IPs.";
+ };
+
+ httpsOnly = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Serve and listen only through HTTPS.";
+ };
+
+ videoPaths = mkOption {
+ type = types.listOf types.path;
+ default = [];
+ example = [ "/home/okina/Videos/tehe_pero.webm" ];
+ description = "Videos that will be symlinked into www/videos.";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ security.sudo.enable = cfg.enable;
+ services.postgresql.enable = cfg.enable;
+ services.postgresql.package = pkgs.postgresql_11;
+ services.meguca.passwordFile = mkDefault (pkgs.writeText "meguca-password-file" cfg.password);
+ services.meguca.postgresArgsFile = mkDefault (pkgs.writeText "meguca-postgres-args" cfg.postgresArgs);
+ services.meguca.postgresArgs = mkDefault "user=meguca password=${cfg.password} dbname=meguca sslmode=disable";
+
+ systemd.services.meguca = {
+ description = "meguca";
+ after = [ "network.target" "postgresql.service" ];
+ wantedBy = [ "multi-user.target" ];
+
+ preStart = ''
+ # Ensure folder exists or create it and links and permissions are correct
+ mkdir -p ${escapeShellArg cfg.dataDir}/www
+ rm -rf ${escapeShellArg cfg.dataDir}/www/videos
+ ln -sf ${pkgs.meguca}/share/meguca/www/* ${escapeShellArg cfg.dataDir}/www
+ unlink ${escapeShellArg cfg.dataDir}/www/videos
+ mkdir -p ${escapeShellArg cfg.dataDir}/www/videos
+
+ for vid in ${escapeShellArg cfg.videoPaths}; do
+ ln -sf $vid ${escapeShellArg cfg.dataDir}/www/videos
+ done
+
+ chmod 750 ${escapeShellArg cfg.dataDir}
+ chown -R meguca:meguca ${escapeShellArg cfg.dataDir}
+
+ # Ensure the database is correct or create it
+ ${pkgs.sudo}/bin/sudo -u ${postgres.superUser} ${postgres.package}/bin/createuser \
+ -SDR meguca || true
+ ${pkgs.sudo}/bin/sudo -u ${postgres.superUser} ${postgres.package}/bin/createdb \
+ -T template0 -E UTF8 -O meguca meguca || true
+ ${pkgs.sudo}/bin/sudo -u meguca ${postgres.package}/bin/psql \
+ -c "ALTER ROLE meguca WITH PASSWORD '$(cat ${escapeShellArg cfg.passwordFile})';" || true
+ '';
+
+ script = ''
+ cd ${escapeShellArg cfg.dataDir}
+
+ ${pkgs.meguca}/bin/meguca -d "$(cat ${escapeShellArg cfg.postgresArgsFile})"''
+ + optionalString (cfg.reverseProxy != null) " -R ${cfg.reverseProxy}"
+ + optionalString (cfg.sslCertificate != null) " -S ${cfg.sslCertificate}"
+ + optionalString (cfg.listenAddress != null) " -a ${cfg.listenAddress}"
+ + optionalString (cfg.cacheSize != null) " -c ${toString cfg.cacheSize}"
+ + optionalString (cfg.compressTraffic) " -g"
+ + optionalString (cfg.assumeReverseProxy) " -r"
+ + optionalString (cfg.httpsOnly) " -s" + " start";
+
+ serviceConfig = {
+ PermissionsStartOnly = true;
+ Type = "forking";
+ User = "meguca";
+ Group = "meguca";
+ ExecStop = "${pkgs.meguca}/bin/meguca stop";
+ };
+ };
+
+ users = {
+ groups.meguca.gid = config.ids.gids.meguca;
+
+ users.meguca = {
+ description = "meguca server service user";
+ home = cfg.dataDir;
+ createHome = true;
+ group = "meguca";
+ uid = config.ids.uids.meguca;
+ };
+ };
+ };
+
+ imports = [
+ (mkRenamedOptionModule [ "services" "meguca" "baseDir" ] [ "services" "meguca" "dataDir" ])
+ ];
+
+ meta.maintainers = with maintainers; [ chiiruno ];
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/mighttpd2.nix b/nixpkgs/nixos/modules/services/web-servers/mighttpd2.nix
new file mode 100644
index 00000000000..f9b1a8b6ccc
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/mighttpd2.nix
@@ -0,0 +1,132 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.services.mighttpd2;
+ configFile = pkgs.writeText "mighty-config" cfg.config;
+ routingFile = pkgs.writeText "mighty-routing" cfg.routing;
+in {
+ options.services.mighttpd2 = {
+ enable = mkEnableOption "Mighttpd2 web server";
+
+ config = mkOption {
+ default = "";
+ example = ''
+ # Example configuration for Mighttpd 2
+ Port: 80
+ # IP address or "*"
+ Host: *
+ Debug_Mode: Yes # Yes or No
+ # If available, "nobody" is much more secure for User:.
+ User: root
+ # If available, "nobody" is much more secure for Group:.
+ Group: root
+ Pid_File: /run/mighty.pid
+ Logging: Yes # Yes or No
+ Log_File: /var/log/mighty # The directory must be writable by User:
+ Log_File_Size: 16777216 # bytes
+ Log_Backup_Number: 10
+ Index_File: index.html
+ Index_Cgi: index.cgi
+ Status_File_Dir: /usr/local/share/mighty/status
+ Connection_Timeout: 30 # seconds
+ Fd_Cache_Duration: 10 # seconds
+ # Server_Name: Mighttpd/3.x.y
+ Tls_Port: 443
+ Tls_Cert_File: cert.pem # should change this with an absolute path
+ # should change this with comma-separated absolute paths
+ Tls_Chain_Files: chain.pem
+ # Currently, Tls_Key_File must not be encrypted.
+ Tls_Key_File: privkey.pem # should change this with an absolute path
+ Service: 0 # 0 is HTTP only, 1 is HTTPS only, 2 is both
+ '';
+ type = types.lines;
+ description = ''
+ Verbatim config file to use
+ (see http://www.mew.org/~kazu/proj/mighttpd/en/config.html)
+ '';
+ };
+
+ routing = mkOption {
+ default = "";
+ example = ''
+ # Example routing for Mighttpd 2
+
+ # Domain lists
+ [localhost www.example.com]
+
+ # Entries are looked up in the specified order
+ # All paths must end with "/"
+
+ # A path to CGI scripts should be specified with "=>"
+ /~alice/cgi-bin/ => /home/alice/public_html/cgi-bin/
+
+ # A path to static files should be specified with "->"
+ /~alice/ -> /home/alice/public_html/
+ /cgi-bin/ => /export/cgi-bin/
+
+ # Reverse proxy rules should be specified with ">>"
+ # /path >> host:port/path2
+ # Either "host" or ":port" can be committed, but not both.
+ /app/cal/ >> example.net/calendar/
+ # Yesod app in the same server
+ /app/wiki/ >> 127.0.0.1:3000/
+
+ / -> /export/www/
+ '';
+ type = types.lines;
+ description = ''
+ Verbatim routing file to use
+ (see http://www.mew.org/~kazu/proj/mighttpd/en/config.html)
+ '';
+ };
+
+ cores = mkOption {
+ default = null;
+ type = types.nullOr types.int;
+ description = ''
+ How many cores to use.
+ If null it will be determined automatically
+ '';
+ };
+
+ };
+
+ config = mkIf cfg.enable {
+ assertions =
+ [ { assertion = cfg.routing != "";
+ message = "You need at least one rule in mighttpd2.routing";
+ }
+ ];
+ systemd.services.mighttpd2 = {
+ description = "Mighttpd2 web server";
+ after = [ "network-online.target" ];
+ wantedBy = [ "multi-user.target" ];
+ serviceConfig = {
+ ExecStart = ''
+ ${pkgs.haskellPackages.mighttpd2}/bin/mighty \
+ ${configFile} \
+ ${routingFile} \
+ +RTS -N${optionalString (cfg.cores != null) "${cfg.cores}"}
+ '';
+ Type = "simple";
+ User = "mighttpd2";
+ Group = "mighttpd2";
+ Restart = "on-failure";
+ AmbientCapabilities = "cap_net_bind_service";
+ CapabilityBoundingSet = "cap_net_bind_service";
+ };
+ };
+
+ users.users.mighttpd2 = {
+ group = "mighttpd2";
+ uid = config.ids.uids.mighttpd2;
+ isSystemUser = true;
+ };
+
+ users.groups.mighttpd2.gid = config.ids.gids.mighttpd2;
+ };
+
+ meta.maintainers = with lib.maintainers; [ fgaz ];
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/minio.nix b/nixpkgs/nixos/modules/services/web-servers/minio.nix
new file mode 100644
index 00000000000..cd123000f00
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/minio.nix
@@ -0,0 +1,108 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.services.minio;
+in
+{
+ meta.maintainers = [ maintainers.bachp ];
+
+ options.services.minio = {
+ enable = mkEnableOption "Minio Object Storage";
+
+ listenAddress = mkOption {
+ default = ":9000";
+ type = types.str;
+ description = "Listen on a specific IP address and port.";
+ };
+
+ dataDir = mkOption {
+ default = "/var/lib/minio/data";
+ type = types.path;
+ description = "The data directory, for storing the objects.";
+ };
+
+ configDir = mkOption {
+ default = "/var/lib/minio/config";
+ type = types.path;
+ description = "The config directory, for the access keys and other settings.";
+ };
+
+ accessKey = mkOption {
+ default = "";
+ type = types.str;
+ description = ''
+ Access key of 5 to 20 characters in length that clients use to access the server.
+ This overrides the access key that is generated by minio on first startup and stored inside the
+ <literal>configDir</literal> directory.
+ '';
+ };
+
+ secretKey = mkOption {
+ default = "";
+ type = types.str;
+ description = ''
+ Specify the Secret key of 8 to 40 characters in length that clients use to access the server.
+ This overrides the secret key that is generated by minio on first startup and stored inside the
+ <literal>configDir</literal> directory.
+ '';
+ };
+
+ region = mkOption {
+ default = "us-east-1";
+ type = types.str;
+ description = ''
+ The physical location of the server. By default it is set to us-east-1, which is same as AWS S3's and Minio's default region.
+ '';
+ };
+
+ browser = mkOption {
+ default = true;
+ type = types.bool;
+ description = "Enable or disable access to web UI.";
+ };
+
+ package = mkOption {
+ default = pkgs.minio;
+ defaultText = "pkgs.minio";
+ type = types.package;
+ description = "Minio package to use.";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ systemd.tmpfiles.rules = [
+ "d '${cfg.configDir}' - minio minio - -"
+ "d '${cfg.dataDir}' - minio minio - -"
+ ];
+
+ systemd.services.minio = {
+ description = "Minio Object Storage";
+ after = [ "network.target" ];
+ wantedBy = [ "multi-user.target" ];
+ serviceConfig = {
+ ExecStart = "${cfg.package}/bin/minio server --json --address ${cfg.listenAddress} --config-dir=${cfg.configDir} ${cfg.dataDir}";
+ Type = "simple";
+ User = "minio";
+ Group = "minio";
+ LimitNOFILE = 65536;
+ };
+ environment = {
+ MINIO_REGION = "${cfg.region}";
+ MINIO_BROWSER = "${if cfg.browser then "on" else "off"}";
+ } // optionalAttrs (cfg.accessKey != "") {
+ MINIO_ACCESS_KEY = "${cfg.accessKey}";
+ } // optionalAttrs (cfg.secretKey != "") {
+ MINIO_SECRET_KEY = "${cfg.secretKey}";
+ };
+ };
+
+ users.users.minio = {
+ group = "minio";
+ uid = config.ids.uids.minio;
+ };
+
+ users.groups.minio.gid = config.ids.uids.minio;
+ };
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix b/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix
new file mode 100644
index 00000000000..e597f34700a
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix
@@ -0,0 +1,709 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.services.nginx;
+ certs = config.security.acme.certs;
+ vhostsConfigs = mapAttrsToList (vhostName: vhostConfig: vhostConfig) virtualHosts;
+ acmeEnabledVhosts = filter (vhostConfig: vhostConfig.enableACME && vhostConfig.useACMEHost == null) vhostsConfigs;
+ virtualHosts = mapAttrs (vhostName: vhostConfig:
+ let
+ serverName = if vhostConfig.serverName != null
+ then vhostConfig.serverName
+ else vhostName;
+ in
+ vhostConfig // {
+ inherit serverName;
+ } // (optionalAttrs vhostConfig.enableACME {
+ sslCertificate = "${certs.${serverName}.directory}/fullchain.pem";
+ sslCertificateKey = "${certs.${serverName}.directory}/key.pem";
+ sslTrustedCertificate = "${certs.${serverName}.directory}/full.pem";
+ }) // (optionalAttrs (vhostConfig.useACMEHost != null) {
+ sslCertificate = "${certs.${vhostConfig.useACMEHost}.directory}/fullchain.pem";
+ sslCertificateKey = "${certs.${vhostConfig.useACMEHost}.directory}/key.pem";
+ sslTrustedCertificate = "${certs.${vhostConfig.useACMEHost}.directory}/fullchain.pem";
+ })
+ ) cfg.virtualHosts;
+ enableIPv6 = config.networking.enableIPv6;
+
+ recommendedProxyConfig = pkgs.writeText "nginx-recommended-proxy-headers.conf" ''
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_set_header X-Forwarded-Host $host;
+ proxy_set_header X-Forwarded-Server $host;
+ proxy_set_header Accept-Encoding "";
+ '';
+
+ upstreamConfig = toString (flip mapAttrsToList cfg.upstreams (name: upstream: ''
+ upstream ${name} {
+ ${toString (flip mapAttrsToList upstream.servers (name: server: ''
+ server ${name} ${optionalString server.backup "backup"};
+ ''))}
+ ${upstream.extraConfig}
+ }
+ ''));
+
+ configFile = pkgs.writers.writeNginxConfig "nginx.conf" ''
+ user ${cfg.user} ${cfg.group};
+ error_log ${cfg.logError};
+ daemon off;
+
+ ${cfg.config}
+
+ ${optionalString (cfg.eventsConfig != "" || cfg.config == "") ''
+ events {
+ ${cfg.eventsConfig}
+ }
+ ''}
+
+ ${optionalString (cfg.httpConfig == "" && cfg.config == "") ''
+ http {
+ include ${cfg.package}/conf/mime.types;
+ include ${cfg.package}/conf/fastcgi.conf;
+ include ${cfg.package}/conf/uwsgi_params;
+
+ ${optionalString (cfg.resolver.addresses != []) ''
+ resolver ${toString cfg.resolver.addresses} ${optionalString (cfg.resolver.valid != "") "valid=${cfg.resolver.valid}"} ${optionalString (!cfg.resolver.ipv6) "ipv6=off"};
+ ''}
+ ${upstreamConfig}
+
+ ${optionalString (cfg.recommendedOptimisation) ''
+ # optimisation
+ sendfile on;
+ tcp_nopush on;
+ tcp_nodelay on;
+ keepalive_timeout 65;
+ types_hash_max_size 2048;
+ ''}
+
+ ssl_protocols ${cfg.sslProtocols};
+ ssl_ciphers ${cfg.sslCiphers};
+ ${optionalString (cfg.sslDhparam != null) "ssl_dhparam ${cfg.sslDhparam};"}
+
+ ${optionalString (cfg.recommendedTlsSettings) ''
+ ssl_session_cache shared:SSL:42m;
+ ssl_session_timeout 23m;
+ ssl_ecdh_curve secp384r1;
+ ssl_prefer_server_ciphers on;
+ ssl_stapling on;
+ ssl_stapling_verify on;
+ ''}
+
+ ${optionalString (cfg.recommendedGzipSettings) ''
+ gzip on;
+ gzip_proxied any;
+ gzip_comp_level 5;
+ gzip_types
+ application/atom+xml
+ application/javascript
+ application/json
+ application/xml
+ application/xml+rss
+ image/svg+xml
+ text/css
+ text/javascript
+ text/plain
+ text/xml;
+ gzip_vary on;
+ ''}
+
+ ${optionalString (cfg.recommendedProxySettings) ''
+ proxy_redirect off;
+ proxy_connect_timeout 90;
+ proxy_send_timeout 90;
+ proxy_read_timeout 90;
+ proxy_http_version 1.0;
+ include ${recommendedProxyConfig};
+ ''}
+
+ # $connection_upgrade is used for websocket proxying
+ map $http_upgrade $connection_upgrade {
+ default upgrade;
+ ''' close;
+ }
+ client_max_body_size ${cfg.clientMaxBodySize};
+
+ server_tokens ${if cfg.serverTokens then "on" else "off"};
+
+ ${cfg.commonHttpConfig}
+
+ ${vhosts}
+
+ ${optionalString cfg.statusPage ''
+ server {
+ listen 80;
+ ${optionalString enableIPv6 "listen [::]:80;" }
+
+ server_name localhost;
+
+ location /nginx_status {
+ stub_status on;
+ access_log off;
+ allow 127.0.0.1;
+ ${optionalString enableIPv6 "allow ::1;"}
+ deny all;
+ }
+ }
+ ''}
+
+ ${cfg.appendHttpConfig}
+ }''}
+
+ ${optionalString (cfg.httpConfig != "") ''
+ http {
+ include ${cfg.package}/conf/mime.types;
+ include ${cfg.package}/conf/fastcgi.conf;
+ include ${cfg.package}/conf/uwsgi_params;
+ ${cfg.httpConfig}
+ }''}
+
+ ${cfg.appendConfig}
+ '';
+
+ configPath = if cfg.enableReload
+ then "/etc/nginx/nginx.conf"
+ else configFile;
+
+ vhosts = concatStringsSep "\n" (mapAttrsToList (vhostName: vhost:
+ let
+ onlySSL = vhost.onlySSL || vhost.enableSSL;
+ hasSSL = onlySSL || vhost.addSSL || vhost.forceSSL;
+
+ defaultListen =
+ if vhost.listen != [] then vhost.listen
+ else ((optionals hasSSL (
+ singleton { addr = "0.0.0.0"; port = 443; ssl = true; }
+ ++ optional enableIPv6 { addr = "[::]"; port = 443; ssl = true; }
+ )) ++ optionals (!onlySSL) (
+ singleton { addr = "0.0.0.0"; port = 80; ssl = false; }
+ ++ optional enableIPv6 { addr = "[::]"; port = 80; ssl = false; }
+ ));
+
+ hostListen =
+ if vhost.forceSSL
+ then filter (x: x.ssl) defaultListen
+ else defaultListen;
+
+ listenString = { addr, port, ssl, extraParameters ? [], ... }:
+ "listen ${addr}:${toString port} "
+ + optionalString ssl "ssl "
+ + optionalString (ssl && vhost.http2) "http2 "
+ + optionalString vhost.default "default_server "
+ + optionalString (extraParameters != []) (concatStringsSep " " extraParameters)
+ + ";";
+
+ redirectListen = filter (x: !x.ssl) defaultListen;
+
+ acmeLocation = optionalString (vhost.enableACME || vhost.useACMEHost != null) ''
+ location /.well-known/acme-challenge {
+ ${optionalString (vhost.acmeFallbackHost != null) "try_files $uri @acme-fallback;"}
+ root ${vhost.acmeRoot};
+ auth_basic off;
+ }
+ ${optionalString (vhost.acmeFallbackHost != null) ''
+ location @acme-fallback {
+ auth_basic off;
+ proxy_pass http://${vhost.acmeFallbackHost};
+ }
+ ''}
+ '';
+
+ in ''
+ ${optionalString vhost.forceSSL ''
+ server {
+ ${concatMapStringsSep "\n" listenString redirectListen}
+
+ server_name ${vhost.serverName} ${concatStringsSep " " vhost.serverAliases};
+ ${acmeLocation}
+ location / {
+ return 301 https://$host$request_uri;
+ }
+ }
+ ''}
+
+ server {
+ ${concatMapStringsSep "\n" listenString hostListen}
+ server_name ${vhost.serverName} ${concatStringsSep " " vhost.serverAliases};
+ ${acmeLocation}
+ ${optionalString (vhost.root != null) "root ${vhost.root};"}
+ ${optionalString (vhost.globalRedirect != null) ''
+ return 301 http${optionalString hasSSL "s"}://${vhost.globalRedirect}$request_uri;
+ ''}
+ ${optionalString hasSSL ''
+ ssl_certificate ${vhost.sslCertificate};
+ ssl_certificate_key ${vhost.sslCertificateKey};
+ ''}
+ ${optionalString (hasSSL && vhost.sslTrustedCertificate != null) ''
+ ssl_trusted_certificate ${vhost.sslTrustedCertificate};
+ ''}
+
+ ${optionalString (vhost.basicAuthFile != null || vhost.basicAuth != {}) ''
+ auth_basic secured;
+ auth_basic_user_file ${if vhost.basicAuthFile != null then vhost.basicAuthFile else mkHtpasswd vhostName vhost.basicAuth};
+ ''}
+
+ ${mkLocations vhost.locations}
+
+ ${vhost.extraConfig}
+ }
+ ''
+ ) virtualHosts);
+ mkLocations = locations: concatStringsSep "\n" (map (config: ''
+ location ${config.location} {
+ ${optionalString (config.proxyPass != null && !cfg.proxyResolveWhileRunning)
+ "proxy_pass ${config.proxyPass};"
+ }
+ ${optionalString (config.proxyPass != null && cfg.proxyResolveWhileRunning) ''
+ set $nix_proxy_target "${config.proxyPass}";
+ proxy_pass $nix_proxy_target;
+ ''}
+ ${optionalString config.proxyWebsockets ''
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection $connection_upgrade;
+ ''}
+ ${optionalString (config.index != null) "index ${config.index};"}
+ ${optionalString (config.tryFiles != null) "try_files ${config.tryFiles};"}
+ ${optionalString (config.root != null) "root ${config.root};"}
+ ${optionalString (config.alias != null) "alias ${config.alias};"}
+ ${optionalString (config.return != null) "return ${config.return};"}
+ ${config.extraConfig}
+ ${optionalString (config.proxyPass != null && cfg.recommendedProxySettings) "include ${recommendedProxyConfig};"}
+ }
+ '') (sortProperties (mapAttrsToList (k: v: v // { location = k; }) locations)));
+ mkHtpasswd = vhostName: authDef: pkgs.writeText "${vhostName}.htpasswd" (
+ concatStringsSep "\n" (mapAttrsToList (user: password: ''
+ ${user}:{PLAIN}${password}
+ '') authDef)
+ );
+in
+
+{
+ options = {
+ services.nginx = {
+ enable = mkEnableOption "Nginx Web Server";
+
+ statusPage = mkOption {
+ default = false;
+ type = types.bool;
+ description = "
+ Enable status page reachable from localhost on http://127.0.0.1/nginx_status.
+ ";
+ };
+
+ recommendedTlsSettings = mkOption {
+ default = false;
+ type = types.bool;
+ description = "
+ Enable recommended TLS settings.
+ ";
+ };
+
+ recommendedOptimisation = mkOption {
+ default = false;
+ type = types.bool;
+ description = "
+ Enable recommended optimisation settings.
+ ";
+ };
+
+ recommendedGzipSettings = mkOption {
+ default = false;
+ type = types.bool;
+ description = "
+ Enable recommended gzip settings.
+ ";
+ };
+
+ recommendedProxySettings = mkOption {
+ default = false;
+ type = types.bool;
+ description = "
+ Enable recommended proxy settings.
+ ";
+ };
+
+ package = mkOption {
+ default = pkgs.nginxStable;
+ defaultText = "pkgs.nginxStable";
+ type = types.package;
+ description = "
+ Nginx package to use. This defaults to the stable version. Note
+ that the nginx team recommends to use the mainline version which
+ available in nixpkgs as <literal>nginxMainline</literal>.
+ ";
+ };
+
+ logError = mkOption {
+ default = "stderr";
+ description = "
+ Configures logging.
+ The first parameter defines a file that will store the log. The
+ special value stderr selects the standard error file. Logging to
+ syslog can be configured by specifying the “syslog:” prefix.
+ The second parameter determines the level of logging, and can be
+ one of the following: debug, info, notice, warn, error, crit,
+ alert, or emerg. Log levels above are listed in the order of
+ increasing severity. Setting a certain log level will cause all
+ messages of the specified and more severe log levels to be logged.
+ If this parameter is omitted then error is used.
+ ";
+ };
+
+ preStart = mkOption {
+ type = types.lines;
+ default = ''
+ test -d ${cfg.stateDir}/logs || mkdir -m 750 -p ${cfg.stateDir}/logs
+ test `stat -c %a ${cfg.stateDir}` = "750" || chmod 750 ${cfg.stateDir}
+ test `stat -c %a ${cfg.stateDir}/logs` = "750" || chmod 750 ${cfg.stateDir}/logs
+ chown -R ${cfg.user}:${cfg.group} ${cfg.stateDir}
+ '';
+ description = "
+ Shell commands executed before the service's nginx is started.
+ ";
+ };
+
+ config = mkOption {
+ default = "";
+ description = "
+ Verbatim nginx.conf configuration.
+ This is mutually exclusive with the structured configuration
+ via virtualHosts and the recommendedXyzSettings configuration
+ options. See appendConfig for appending to the generated http block.
+ ";
+ };
+
+ appendConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = ''
+ Configuration lines appended to the generated Nginx
+ configuration file. Commonly used by different modules
+ providing http snippets. <option>appendConfig</option>
+ can be specified more than once and it's value will be
+ concatenated (contrary to <option>config</option> which
+ can be set only once).
+ '';
+ };
+
+ commonHttpConfig = mkOption {
+ type = types.lines;
+ default = "";
+ example = ''
+ resolver 127.0.0.1 valid=5s;
+
+ log_format myformat '$remote_addr - $remote_user [$time_local] '
+ '"$request" $status $body_bytes_sent '
+ '"$http_referer" "$http_user_agent"';
+ '';
+ description = ''
+ With nginx you must provide common http context definitions before
+ they are used, e.g. log_format, resolver, etc. inside of server
+ or location contexts. Use this attribute to set these definitions
+ at the appropriate location.
+ '';
+ };
+
+ httpConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = "
+ Configuration lines to be set inside the http block.
+ This is mutually exclusive with the structured configuration
+ via virtualHosts and the recommendedXyzSettings configuration
+ options. See appendHttpConfig for appending to the generated http block.
+ ";
+ };
+
+ eventsConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = ''
+ Configuration lines to be set inside the events block.
+ '';
+ };
+
+ appendHttpConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = "
+ Configuration lines to be appended to the generated http block.
+ This is mutually exclusive with using config and httpConfig for
+ specifying the whole http block verbatim.
+ ";
+ };
+
+ enableReload = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Reload nginx when configuration file changes (instead of restart).
+ The configuration file is exposed at <filename>/etc/nginx/nginx.conf</filename>.
+ See also <literal>systemd.services.*.restartIfChanged</literal>.
+ '';
+ };
+
+ stateDir = mkOption {
+ default = "/var/spool/nginx";
+ description = "
+ Directory holding all state for nginx to run.
+ ";
+ };
+
+ user = mkOption {
+ type = types.str;
+ default = "nginx";
+ description = "User account under which nginx runs.";
+ };
+
+ group = mkOption {
+ type = types.str;
+ default = "nginx";
+ description = "Group account under which nginx runs.";
+ };
+
+ serverTokens = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Show nginx version in headers and error pages.";
+ };
+
+ clientMaxBodySize = mkOption {
+ type = types.str;
+ default = "10m";
+ description = "Set nginx global client_max_body_size.";
+ };
+
+ sslCiphers = mkOption {
+ type = types.str;
+ default = "EECDH+aRSA+AESGCM:EDH+aRSA:EECDH+aRSA:+AES256:+AES128:+SHA1:!CAMELLIA:!SEED:!3DES:!DES:!RC4:!eNULL";
+ description = "Ciphers to choose from when negotiating tls handshakes.";
+ };
+
+ sslProtocols = mkOption {
+ type = types.str;
+ default = "TLSv1.2 TLSv1.3";
+ example = "TLSv1 TLSv1.1 TLSv1.2 TLSv1.3";
+ description = "Allowed TLS protocol versions.";
+ };
+
+ sslDhparam = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ example = "/path/to/dhparams.pem";
+ description = "Path to DH parameters file.";
+ };
+
+ proxyResolveWhileRunning = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Resolves domains of proxyPass targets at runtime
+ and not only at start, you have to set
+ services.nginx.resolver, too.
+ '';
+ };
+
+ resolver = mkOption {
+ type = types.submodule {
+ options = {
+ addresses = mkOption {
+ type = types.listOf types.str;
+ default = [];
+ example = literalExample ''[ "[::1]" "127.0.0.1:5353" ]'';
+ description = "List of resolvers to use";
+ };
+ valid = mkOption {
+ type = types.str;
+ default = "";
+ example = "30s";
+ description = ''
+ By default, nginx caches answers using the TTL value of a response.
+ An optional valid parameter allows overriding it
+ '';
+ };
+ ipv6 = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ By default, nginx will look up both IPv4 and IPv6 addresses while resolving.
+ If looking up of IPv6 addresses is not desired, the ipv6=off parameter can be
+ specified.
+ '';
+ };
+ };
+ };
+ description = ''
+ Configures name servers used to resolve names of upstream servers into addresses
+ '';
+ default = {};
+ };
+
+ upstreams = mkOption {
+ type = types.attrsOf (types.submodule {
+ options = {
+ servers = mkOption {
+ type = types.attrsOf (types.submodule {
+ options = {
+ backup = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Marks the server as a backup server. It will be passed
+ requests when the primary servers are unavailable.
+ '';
+ };
+ };
+ });
+ description = ''
+ Defines the address and other parameters of the upstream servers.
+ '';
+ default = {};
+ };
+ extraConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = ''
+ These lines go to the end of the upstream verbatim.
+ '';
+ };
+ };
+ });
+ description = ''
+ Defines a group of servers to use as proxy target.
+ '';
+ default = {};
+ };
+
+ virtualHosts = mkOption {
+ type = types.attrsOf (types.submodule (import ./vhost-options.nix {
+ inherit config lib;
+ }));
+ default = {
+ localhost = {};
+ };
+ example = literalExample ''
+ {
+ "hydra.example.com" = {
+ forceSSL = true;
+ enableACME = true;
+ locations."/" = {
+ proxyPass = "http://localhost:3000";
+ };
+ };
+ };
+ '';
+ description = "Declarative vhost config";
+ };
+ };
+ };
+
+ config = mkIf cfg.enable {
+ # TODO: test user supplied config file pases syntax test
+
+ warnings =
+ let
+ deprecatedSSL = name: config: optional config.enableSSL
+ ''
+ config.services.nginx.virtualHosts.<name>.enableSSL is deprecated,
+ use config.services.nginx.virtualHosts.<name>.onlySSL instead.
+ '';
+
+ in flatten (mapAttrsToList deprecatedSSL virtualHosts);
+
+ assertions =
+ let
+ hostOrAliasIsNull = l: l.root == null || l.alias == null;
+ in [
+ {
+ assertion = all (host: all hostOrAliasIsNull (attrValues host.locations)) (attrValues virtualHosts);
+ message = "Only one of nginx root or alias can be specified on a location.";
+ }
+
+ {
+ assertion = all (conf: with conf;
+ !(addSSL && (onlySSL || enableSSL)) &&
+ !(forceSSL && (onlySSL || enableSSL)) &&
+ !(addSSL && forceSSL)
+ ) (attrValues virtualHosts);
+ message = ''
+ Options services.nginx.service.virtualHosts.<name>.addSSL,
+ services.nginx.virtualHosts.<name>.onlySSL and services.nginx.virtualHosts.<name>.forceSSL
+ are mutually exclusive.
+ '';
+ }
+
+ {
+ assertion = all (conf: !(conf.enableACME && conf.useACMEHost != null)) (attrValues virtualHosts);
+ message = ''
+ Options services.nginx.service.virtualHosts.<name>.enableACME and
+ services.nginx.virtualHosts.<name>.useACMEHost are mutually exclusive.
+ '';
+ }
+ ];
+
+ systemd.services.nginx = {
+ description = "Nginx Web Server";
+ wantedBy = [ "multi-user.target" ];
+ wants = concatLists (map (vhostConfig: ["acme-${vhostConfig.serverName}.service" "acme-selfsigned-${vhostConfig.serverName}.service"]) acmeEnabledVhosts);
+ after = [ "network.target" ] ++ map (vhostConfig: "acme-selfsigned-${vhostConfig.serverName}.service") acmeEnabledVhosts;
+ stopIfChanged = false;
+ preStart =
+ ''
+ ${cfg.preStart}
+ ${cfg.package}/bin/nginx -c ${configPath} -p ${cfg.stateDir} -t
+ '';
+ serviceConfig = {
+ ExecStart = "${cfg.package}/bin/nginx -c ${configPath} -p ${cfg.stateDir}";
+ ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+ Restart = "always";
+ RestartSec = "10s";
+ StartLimitInterval = "1min";
+ };
+ };
+
+ environment.etc."nginx/nginx.conf" = mkIf cfg.enableReload {
+ source = configFile;
+ };
+
+ systemd.services.nginx-config-reload = mkIf cfg.enableReload {
+ wantedBy = [ "nginx.service" ];
+ restartTriggers = [ configFile ];
+ script = ''
+ if ${pkgs.systemd}/bin/systemctl -q is-active nginx.service ; then
+ ${pkgs.systemd}/bin/systemctl reload nginx.service
+ fi
+ '';
+ serviceConfig.RemainAfterExit = true;
+ };
+
+ security.acme.certs = filterAttrs (n: v: v != {}) (
+ let
+ acmePairs = map (vhostConfig: { name = vhostConfig.serverName; value = {
+ user = cfg.user;
+ group = lib.mkDefault cfg.group;
+ webroot = vhostConfig.acmeRoot;
+ extraDomains = genAttrs vhostConfig.serverAliases (alias: null);
+ postRun = ''
+ systemctl reload nginx
+ '';
+ }; }) acmeEnabledVhosts;
+ in
+ listToAttrs acmePairs
+ );
+
+ users.users = optionalAttrs (cfg.user == "nginx") (singleton
+ { name = "nginx";
+ group = cfg.group;
+ uid = config.ids.uids.nginx;
+ });
+
+ users.groups = optionalAttrs (cfg.group == "nginx") (singleton
+ { name = "nginx";
+ gid = config.ids.gids.nginx;
+ });
+ };
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/nginx/gitweb.nix b/nixpkgs/nixos/modules/services/web-servers/nginx/gitweb.nix
new file mode 100644
index 00000000000..272fd148018
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/nginx/gitweb.nix
@@ -0,0 +1,61 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.services.gitweb;
+ package = pkgs.gitweb.override (optionalAttrs cfg.gitwebTheme {
+ gitwebTheme = true;
+ });
+
+in
+{
+
+ options.services.nginx.gitweb = {
+
+ enable = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ If true, enable gitweb in nginx. Access it at http://yourserver/gitweb
+ '';
+ };
+
+ };
+
+ config = mkIf config.services.nginx.gitweb.enable {
+
+ systemd.services.gitweb = {
+ description = "GitWeb service";
+ script = "${package}/gitweb.cgi --fastcgi --nproc=1";
+ environment = {
+ FCGI_SOCKET_PATH = "/run/gitweb/gitweb.sock";
+ };
+ serviceConfig = {
+ User = "nginx";
+ Group = "nginx";
+ RuntimeDirectory = [ "gitweb" ];
+ };
+ wantedBy = [ "multi-user.target" ];
+ };
+
+ services.nginx = {
+ virtualHosts.default = {
+ locations."/gitweb/static/" = {
+ alias = "${package}/static/";
+ };
+ locations."/gitweb/" = {
+ extraConfig = ''
+ include ${pkgs.nginx}/conf/fastcgi_params;
+ fastcgi_param GITWEB_CONFIG ${cfg.gitwebConfigFile};
+ fastcgi_pass unix:/run/gitweb/gitweb.sock;
+ '';
+ };
+ };
+ };
+
+ };
+
+ meta.maintainers = with maintainers; [ gnidorah ];
+
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/nginx/location-options.nix b/nixpkgs/nixos/modules/services/web-servers/nginx/location-options.nix
new file mode 100644
index 00000000000..aeb9b1dd79e
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/nginx/location-options.nix
@@ -0,0 +1,95 @@
+# This file defines the options that can be used both for the Apache
+# main server configuration, and for the virtual hosts. (The latter
+# has additional options that affect the web server as a whole, like
+# the user/group to run under.)
+
+{ lib }:
+
+with lib;
+
+{
+ options = {
+ proxyPass = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ example = "http://www.example.org/";
+ description = ''
+ Adds proxy_pass directive and sets recommended proxy headers if
+ recommendedProxySettings is enabled.
+ '';
+ };
+
+ proxyWebsockets = mkOption {
+ type = types.bool;
+ default = false;
+ example = true;
+ description = ''
+ Whether to supporty proxying websocket connections with HTTP/1.1.
+ '';
+ };
+
+ index = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ example = "index.php index.html";
+ description = ''
+ Adds index directive.
+ '';
+ };
+
+ tryFiles = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ example = "$uri =404";
+ description = ''
+ Adds try_files directive.
+ '';
+ };
+
+ root = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ example = "/your/root/directory";
+ description = ''
+ Root directory for requests.
+ '';
+ };
+
+ alias = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ example = "/your/alias/directory";
+ description = ''
+ Alias directory for requests.
+ '';
+ };
+
+ return = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ example = "301 http://example.com$request_uri;";
+ description = ''
+ Adds a return directive, for e.g. redirections.
+ '';
+ };
+
+ extraConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = ''
+ These lines go to the end of the location verbatim.
+ '';
+ };
+
+ priority = mkOption {
+ type = types.int;
+ default = 1000;
+ description = ''
+ Order of this location block in relation to the others in the vhost.
+ The semantics are the same as with `lib.mkOrder`. Smaller values have
+ a greater priority.
+ '';
+ };
+ };
+}
+
diff --git a/nixpkgs/nixos/modules/services/web-servers/nginx/vhost-options.nix b/nixpkgs/nixos/modules/services/web-servers/nginx/vhost-options.nix
new file mode 100644
index 00000000000..15b933c984a
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/nginx/vhost-options.nix
@@ -0,0 +1,228 @@
+# This file defines the options that can be used both for the Apache
+# main server configuration, and for the virtual hosts. (The latter
+# has additional options that affect the web server as a whole, like
+# the user/group to run under.)
+
+{ lib, ... }:
+
+with lib;
+{
+ options = {
+ serverName = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = ''
+ Name of this virtual host. Defaults to attribute name in virtualHosts.
+ '';
+ example = "example.org";
+ };
+
+ serverAliases = mkOption {
+ type = types.listOf types.str;
+ default = [];
+ example = ["www.example.org" "example.org"];
+ description = ''
+ Additional names of virtual hosts served by this virtual host configuration.
+ '';
+ };
+
+ listen = mkOption {
+ type = with types; listOf (submodule { options = {
+ addr = mkOption { type = str; description = "IP address."; };
+ port = mkOption { type = int; description = "Port number."; default = 80; };
+ ssl = mkOption { type = bool; description = "Enable SSL."; default = false; };
+ extraParameters = mkOption { type = listOf str; description = "Extra parameters of this listen directive."; default = []; example = [ "reuseport" "deferred" ]; };
+ }; });
+ default = [];
+ example = [
+ { addr = "195.154.1.1"; port = 443; ssl = true;}
+ { addr = "192.154.1.1"; port = 80; }
+ ];
+ description = ''
+ Listen addresses and ports for this virtual host.
+ IPv6 addresses must be enclosed in square brackets.
+ Note: this option overrides <literal>addSSL</literal>
+ and <literal>onlySSL</literal>.
+ '';
+ };
+
+ enableACME = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Whether to ask Let's Encrypt to sign a certificate for this vhost.
+ Alternately, you can use an existing certificate through <option>useACMEHost</option>.
+ '';
+ };
+
+ useACMEHost = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = ''
+ A host of an existing Let's Encrypt certificate to use.
+ This is useful if you have many subdomains and want to avoid hitting the
+ <link xlink:href="https://letsencrypt.org/docs/rate-limits/">rate limit</link>.
+ Alternately, you can generate a certificate through <option>enableACME</option>.
+ <emphasis>Note that this option does not create any certificates, nor it does add subdomains to existing ones – you will need to create them manually using <xref linkend="opt-security.acme.certs"/>.</emphasis>
+ '';
+ };
+
+ acmeRoot = mkOption {
+ type = types.str;
+ default = "/var/lib/acme/acme-challenge";
+ description = "Directory for the acme challenge which is PUBLIC, don't put certs or keys in here";
+ };
+
+ acmeFallbackHost = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = ''
+ Host which to proxy requests to if acme challenge is not found. Useful
+ if you want multiple hosts to be able to verify the same domain name.
+ '';
+ };
+
+ addSSL = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Whether to enable HTTPS in addition to plain HTTP. This will set defaults for
+ <literal>listen</literal> to listen on all interfaces on the respective default
+ ports (80, 443).
+ '';
+ };
+
+ onlySSL = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Whether to enable HTTPS and reject plain HTTP connections. This will set
+ defaults for <literal>listen</literal> to listen on all interfaces on port 443.
+ '';
+ };
+
+ enableSSL = mkOption {
+ type = types.bool;
+ visible = false;
+ default = false;
+ };
+
+ forceSSL = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Whether to add a separate nginx server block that permanently redirects (301)
+ all plain HTTP traffic to HTTPS. This will set defaults for
+ <literal>listen</literal> to listen on all interfaces on the respective default
+ ports (80, 443), where the non-SSL listens are used for the redirect vhosts.
+ '';
+ };
+
+ sslCertificate = mkOption {
+ type = types.path;
+ example = "/var/host.cert";
+ description = "Path to server SSL certificate.";
+ };
+
+ sslCertificateKey = mkOption {
+ type = types.path;
+ example = "/var/host.key";
+ description = "Path to server SSL certificate key.";
+ };
+
+ sslTrustedCertificate = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ example = "/var/root.cert";
+ description = "Path to root SSL certificate for stapling and client certificates.";
+ };
+
+ http2 = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ Whether to enable HTTP 2.
+ Note that (as of writing) due to nginx's implementation, to disable
+ HTTP 2 you have to disable it on all vhosts that use a given
+ IP address / port.
+ If there is one server block configured to enable http2,then it is
+ enabled for all server blocks on this IP.
+ See https://stackoverflow.com/a/39466948/263061.
+ '';
+ };
+
+ root = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ example = "/data/webserver/docs";
+ description = ''
+ The path of the web root directory.
+ '';
+ };
+
+ default = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Makes this vhost the default.
+ '';
+ };
+
+ extraConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = ''
+ These lines go to the end of the vhost verbatim.
+ '';
+ };
+
+ globalRedirect = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ example = "newserver.example.org";
+ description = ''
+ If set, all requests for this host are redirected permanently to
+ the given hostname.
+ '';
+ };
+
+ basicAuth = mkOption {
+ type = types.attrsOf types.str;
+ default = {};
+ example = literalExample ''
+ {
+ user = "password";
+ };
+ '';
+ description = ''
+ Basic Auth protection for a vhost.
+
+ WARNING: This is implemented to store the password in plain text in the
+ nix store.
+ '';
+ };
+
+ basicAuthFile = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ description = ''
+ Basic Auth password file for a vhost.
+ '';
+ };
+
+ locations = mkOption {
+ type = types.attrsOf (types.submodule (import ./location-options.nix {
+ inherit lib;
+ }));
+ default = {};
+ example = literalExample ''
+ {
+ "/" = {
+ proxyPass = "http://localhost:3000";
+ };
+ };
+ '';
+ description = "Declarative location config";
+ };
+ };
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/phpfpm/default.nix b/nixpkgs/nixos/modules/services/web-servers/phpfpm/default.nix
new file mode 100644
index 00000000000..4ab7e3f0c0a
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/phpfpm/default.nix
@@ -0,0 +1,279 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.services.phpfpm;
+
+ runtimeDir = "/run/phpfpm";
+
+ toStr = value:
+ if true == value then "yes"
+ else if false == value then "no"
+ else toString value;
+
+ fpmCfgFile = pool: poolOpts: pkgs.writeText "phpfpm-${pool}.conf" ''
+ [global]
+ ${concatStringsSep "\n" (mapAttrsToList (n: v: "${n} = ${toStr v}") cfg.settings)}
+ ${optionalString (cfg.extraConfig != null) cfg.extraConfig}
+
+ [${pool}]
+ ${concatStringsSep "\n" (mapAttrsToList (n: v: "${n} = ${toStr v}") poolOpts.settings)}
+ ${concatStringsSep "\n" (mapAttrsToList (n: v: "env[${n}] = ${toStr v}") poolOpts.phpEnv)}
+ ${optionalString (poolOpts.extraConfig != null) poolOpts.extraConfig}
+ '';
+
+ phpIni = poolOpts: pkgs.runCommand "php.ini" {
+ inherit (poolOpts) phpPackage phpOptions;
+ preferLocalBuild = true;
+ nixDefaults = ''
+ sendmail_path = "/run/wrappers/bin/sendmail -t -i"
+ '';
+ passAsFile = [ "nixDefaults" "phpOptions" ];
+ } ''
+ cat $phpPackage/etc/php.ini $nixDefaultsPath $phpOptionsPath > $out
+ '';
+
+ poolOpts = { name, ... }:
+ let
+ poolOpts = cfg.pools.${name};
+ in
+ {
+ options = {
+ socket = mkOption {
+ type = types.str;
+ readOnly = true;
+ description = ''
+ Path to the unix socket file on which to accept FastCGI requests.
+ <note><para>This option is read-only and managed by NixOS.</para></note>
+ '';
+ };
+
+ listen = mkOption {
+ type = types.str;
+ default = "";
+ example = "/path/to/unix/socket";
+ description = ''
+ The address on which to accept FastCGI requests.
+ '';
+ };
+
+ phpPackage = mkOption {
+ type = types.package;
+ default = cfg.phpPackage;
+ defaultText = "config.services.phpfpm.phpPackage";
+ description = ''
+ The PHP package to use for running this PHP-FPM pool.
+ '';
+ };
+
+ phpOptions = mkOption {
+ type = types.lines;
+ default = cfg.phpOptions;
+ defaultText = "config.services.phpfpm.phpOptions";
+ description = ''
+ "Options appended to the PHP configuration file <filename>php.ini</filename> used for this PHP-FPM pool."
+ '';
+ };
+
+ phpEnv = lib.mkOption {
+ type = with types; attrsOf str;
+ default = {};
+ description = ''
+ Environment variables used for this PHP-FPM pool.
+ '';
+ example = literalExample ''
+ {
+ HOSTNAME = "$HOSTNAME";
+ TMP = "/tmp";
+ TMPDIR = "/tmp";
+ TEMP = "/tmp";
+ }
+ '';
+ };
+
+ user = mkOption {
+ type = types.str;
+ description = "User account under which this pool runs.";
+ };
+
+ group = mkOption {
+ type = types.str;
+ description = "Group account under which this pool runs.";
+ };
+
+ settings = mkOption {
+ type = with types; attrsOf (oneOf [ str int bool ]);
+ default = {};
+ description = ''
+ PHP-FPM pool directives. Refer to the "List of pool directives" section of
+ <link xlink:href="https://www.php.net/manual/en/install.fpm.configuration.php"/>
+ for details. Note that settings names must be enclosed in quotes (e.g.
+ <literal>"pm.max_children"</literal> instead of <literal>pm.max_children</literal>).
+ '';
+ example = literalExample ''
+ {
+ "pm" = "dynamic";
+ "pm.max_children" = 75;
+ "pm.start_servers" = 10;
+ "pm.min_spare_servers" = 5;
+ "pm.max_spare_servers" = 20;
+ "pm.max_requests" = 500;
+ }
+ '';
+ };
+
+ extraConfig = mkOption {
+ type = with types; nullOr lines;
+ default = null;
+ description = ''
+ Extra lines that go into the pool configuration.
+ See the documentation on <literal>php-fpm.conf</literal> for
+ details on configuration directives.
+ '';
+ };
+ };
+
+ config = {
+ socket = if poolOpts.listen == "" then "${runtimeDir}/${name}.sock" else poolOpts.listen;
+ group = mkDefault poolOpts.user;
+
+ settings = mapAttrs (name: mkDefault){
+ listen = poolOpts.socket;
+ user = poolOpts.user;
+ group = poolOpts.group;
+ };
+ };
+ };
+
+in {
+
+ options = {
+ services.phpfpm = {
+ settings = mkOption {
+ type = with types; attrsOf (oneOf [ str int bool ]);
+ default = {};
+ description = ''
+ PHP-FPM global directives. Refer to the "List of global php-fpm.conf directives" section of
+ <link xlink:href="https://www.php.net/manual/en/install.fpm.configuration.php"/>
+ for details. Note that settings names must be enclosed in quotes (e.g.
+ <literal>"pm.max_children"</literal> instead of <literal>pm.max_children</literal>).
+ You need not specify the options <literal>error_log</literal> or
+ <literal>daemonize</literal> here, since they are generated by NixOS.
+ '';
+ };
+
+ extraConfig = mkOption {
+ type = with types; nullOr lines;
+ default = null;
+ description = ''
+ Extra configuration that should be put in the global section of
+ the PHP-FPM configuration file. Do not specify the options
+ <literal>error_log</literal> or
+ <literal>daemonize</literal> here, since they are generated by
+ NixOS.
+ '';
+ };
+
+ phpPackage = mkOption {
+ type = types.package;
+ default = pkgs.php;
+ defaultText = "pkgs.php";
+ description = ''
+ The PHP package to use for running the PHP-FPM service.
+ '';
+ };
+
+ phpOptions = mkOption {
+ type = types.lines;
+ default = "";
+ example =
+ ''
+ date.timezone = "CET"
+ '';
+ description = ''
+ Options appended to the PHP configuration file <filename>php.ini</filename>.
+ '';
+ };
+
+ pools = mkOption {
+ type = types.attrsOf (types.submodule poolOpts);
+ default = {};
+ example = literalExample ''
+ {
+ mypool = {
+ user = "php";
+ group = "php";
+ phpPackage = pkgs.php;
+ settings = '''
+ "pm" = "dynamic";
+ "pm.max_children" = 75;
+ "pm.start_servers" = 10;
+ "pm.min_spare_servers" = 5;
+ "pm.max_spare_servers" = 20;
+ "pm.max_requests" = 500;
+ ''';
+ }
+ }'';
+ description = ''
+ PHP-FPM pools. If no pools are defined, the PHP-FPM
+ service is disabled.
+ '';
+ };
+ };
+ };
+
+ config = mkIf (cfg.pools != {}) {
+
+ warnings =
+ mapAttrsToList (pool: poolOpts: ''
+ Using config.services.phpfpm.pools.${pool}.listen is deprecated and will become unsupported in a future release. Please reference the read-only option config.services.phpfpm.pools.${pool}.socket to access the path of your socket.
+ '') (filterAttrs (pool: poolOpts: poolOpts.listen != "") cfg.pools) ++
+ mapAttrsToList (pool: poolOpts: ''
+ Using config.services.phpfpm.pools.${pool}.extraConfig is deprecated and will become unsupported in a future release. Please migrate your configuration to config.services.phpfpm.pools.${pool}.settings.
+ '') (filterAttrs (pool: poolOpts: poolOpts.extraConfig != null) cfg.pools) ++
+ optional (cfg.extraConfig != null) ''
+ Using config.services.phpfpm.extraConfig is deprecated and will become unsupported in a future release. Please migrate your configuration to config.services.phpfpm.settings.
+ ''
+ ;
+
+ services.phpfpm.settings = {
+ error_log = "syslog";
+ daemonize = false;
+ };
+
+ systemd.slices.phpfpm = {
+ description = "PHP FastCGI Process manager pools slice";
+ };
+
+ systemd.targets.phpfpm = {
+ description = "PHP FastCGI Process manager pools target";
+ wantedBy = [ "multi-user.target" ];
+ };
+
+ systemd.services = mapAttrs' (pool: poolOpts:
+ nameValuePair "phpfpm-${pool}" {
+ description = "PHP FastCGI Process Manager service for pool ${pool}";
+ after = [ "network.target" ];
+ wantedBy = [ "phpfpm.target" ];
+ partOf = [ "phpfpm.target" ];
+ serviceConfig = let
+ cfgFile = fpmCfgFile pool poolOpts;
+ iniFile = phpIni poolOpts;
+ in {
+ Slice = "phpfpm.slice";
+ PrivateDevices = true;
+ ProtectSystem = "full";
+ ProtectHome = true;
+ # XXX: We need AF_NETLINK to make the sendmail SUID binary from postfix work
+ RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6 AF_NETLINK";
+ Type = "notify";
+ ExecStart = "${poolOpts.phpPackage}/bin/php-fpm -y ${cfgFile} -c ${iniFile}";
+ ExecReload = "${pkgs.coreutils}/bin/kill -USR2 $MAINPID";
+ RuntimeDirectory = "phpfpm";
+ RuntimeDirectoryPreserve = true; # Relevant when multiple processes are running
+ };
+ }
+ ) cfg.pools;
+ };
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/shellinabox.nix b/nixpkgs/nixos/modules/services/web-servers/shellinabox.nix
new file mode 100644
index 00000000000..58a02ac59c3
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/shellinabox.nix
@@ -0,0 +1,122 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.services.shellinabox;
+
+ # If a certificate file is specified, shellinaboxd requires
+ # a file descriptor to retrieve it
+ fd = "3";
+ createFd = optionalString (cfg.certFile != null) "${fd}<${cfg.certFile}";
+
+ # Command line arguments for the shellinabox daemon
+ args = [ "--background" ]
+ ++ optional (! cfg.enableSSL) "--disable-ssl"
+ ++ optional (cfg.certFile != null) "--cert-fd=${fd}"
+ ++ optional (cfg.certDirectory != null) "--cert=${cfg.certDirectory}"
+ ++ cfg.extraOptions;
+
+ # Command to start shellinaboxd
+ cmd = "${pkgs.shellinabox}/bin/shellinaboxd ${concatStringsSep " " args}";
+
+ # Command to start shellinaboxd if certFile is specified
+ wrappedCmd = "${pkgs.bash}/bin/bash -c 'exec ${createFd} && ${cmd}'";
+
+in
+
+{
+
+ ###### interface
+
+ options = {
+ services.shellinabox = {
+ enable = mkEnableOption "shellinabox daemon";
+
+ user = mkOption {
+ type = types.str;
+ default = "root";
+ description = ''
+ User to run shellinaboxd as. If started as root, the server drops
+ privileges by changing to nobody, unless overridden by the
+ <literal>--user</literal> option.
+ '';
+ };
+
+ enableSSL = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Whether or not to enable SSL (https) support.
+ '';
+ };
+
+ certDirectory = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ example = "/var/certs";
+ description = ''
+ The daemon will look in this directory far any certificates.
+ If the browser negotiated a Server Name Identification the daemon
+ will look for a matching certificate-SERVERNAME.pem file. If no SNI
+ handshake takes place, it will fall back on using the certificate in the
+ certificate.pem file.
+
+ If no suitable certificate is installed, shellinaboxd will attempt to
+ create a new self-signed certificate. This will only succeed if, after
+ dropping privileges, shellinaboxd has write permissions for this
+ directory.
+ '';
+ };
+
+ certFile = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ example = "/var/certificate.pem";
+ description = "Path to server SSL certificate.";
+ };
+
+ extraOptions = mkOption {
+ type = types.listOf types.str;
+ default = [ ];
+ example = [ "--port=443" "--service /:LOGIN" ];
+ description = ''
+ A list of strings to be appended to the command line arguments
+ for shellinaboxd. Please see the manual page
+ <link xlink:href="https://code.google.com/p/shellinabox/wiki/shellinaboxd_man"/>
+ for a full list of available arguments.
+ '';
+ };
+
+ };
+ };
+
+ ###### implementation
+
+ config = mkIf cfg.enable {
+
+ assertions =
+ [ { assertion = cfg.enableSSL == true
+ -> cfg.certDirectory != null || cfg.certFile != null;
+ message = "SSL is enabled for shellinabox, but no certDirectory or certFile has been specefied."; }
+ { assertion = ! (cfg.certDirectory != null && cfg.certFile != null);
+ message = "Cannot set both certDirectory and certFile for shellinabox."; }
+ ];
+
+ systemd.services.shellinaboxd = {
+ description = "Shellinabox Web Server Daemon";
+
+ wantedBy = [ "multi-user.target" ];
+ requires = [ "sshd.service" ];
+ after = [ "sshd.service" ];
+
+ serviceConfig = {
+ Type = "forking";
+ User = "${cfg.user}";
+ ExecStart = "${if cfg.certFile == null then "${cmd}" else "${wrappedCmd}"}";
+ ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+ };
+ };
+ };
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/tomcat.nix b/nixpkgs/nixos/modules/services/web-servers/tomcat.nix
new file mode 100644
index 00000000000..68261c50324
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/tomcat.nix
@@ -0,0 +1,425 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.services.tomcat;
+ tomcat = cfg.package;
+in
+
+{
+
+ meta = {
+ maintainers = with maintainers; [ danbst ];
+ };
+
+ ###### interface
+
+ options = {
+
+ services.tomcat = {
+ enable = mkEnableOption "Apache Tomcat";
+
+ package = mkOption {
+ type = types.package;
+ default = pkgs.tomcat85;
+ defaultText = "pkgs.tomcat85";
+ example = lib.literalExample "pkgs.tomcat9";
+ description = ''
+ Which tomcat package to use.
+ '';
+ };
+
+ purifyOnStart = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ On startup, the `baseDir` directory is populated with various files,
+ subdirectories and symlinks. If this option is enabled, these items
+ (except for the `logs` and `work` subdirectories) are first removed.
+ This prevents interference from remainders of an old configuration
+ (libraries, webapps, etc.), so it's recommended to enable this option.
+ '';
+ };
+
+ baseDir = mkOption {
+ type = lib.types.path;
+ default = "/var/tomcat";
+ description = ''
+ Location where Tomcat stores configuration files, web applications
+ and logfiles. Note that it is partially cleared on each service startup
+ if `purifyOnStart` is enabled.
+ '';
+ };
+
+ logDirs = mkOption {
+ default = [];
+ type = types.listOf types.path;
+ description = "Directories to create in baseDir/logs/";
+ };
+
+ extraConfigFiles = mkOption {
+ default = [];
+ type = types.listOf types.path;
+ description = "Extra configuration files to pull into the tomcat conf directory";
+ };
+
+ extraEnvironment = mkOption {
+ type = types.listOf types.str;
+ default = [];
+ example = [ "ENVIRONMENT=production" ];
+ description = "Environment Variables to pass to the tomcat service";
+ };
+
+ extraGroups = mkOption {
+ default = [];
+ example = [ "users" ];
+ description = "Defines extra groups to which the tomcat user belongs.";
+ };
+
+ user = mkOption {
+ type = types.str;
+ default = "tomcat";
+ description = "User account under which Apache Tomcat runs.";
+ };
+
+ group = mkOption {
+ type = types.str;
+ default = "tomcat";
+ description = "Group account under which Apache Tomcat runs.";
+ };
+
+ javaOpts = mkOption {
+ type = types.either (types.listOf types.str) types.str;
+ default = "";
+ description = "Parameters to pass to the Java Virtual Machine which spawns Apache Tomcat";
+ };
+
+ catalinaOpts = mkOption {
+ type = types.either (types.listOf types.str) types.str;
+ default = "";
+ description = "Parameters to pass to the Java Virtual Machine which spawns the Catalina servlet container";
+ };
+
+ sharedLibs = mkOption {
+ type = types.listOf types.str;
+ default = [];
+ description = "List containing JAR files or directories with JAR files which are libraries shared by the web applications";
+ };
+
+ serverXml = mkOption {
+ type = types.lines;
+ default = "";
+ description = "
+ Verbatim server.xml configuration.
+ This is mutually exclusive with the virtualHosts options.
+ ";
+ };
+
+ commonLibs = mkOption {
+ type = types.listOf types.str;
+ default = [];
+ description = "List containing JAR files or directories with JAR files which are libraries shared by the web applications and the servlet container";
+ };
+
+ webapps = mkOption {
+ type = types.listOf types.path;
+ default = [ tomcat.webapps ];
+ defaultText = "[ pkgs.tomcat85.webapps ]";
+ description = "List containing WAR files or directories with WAR files which are web applications to be deployed on Tomcat";
+ };
+
+ virtualHosts = mkOption {
+ type = types.listOf (types.submodule {
+ options = {
+ name = mkOption {
+ type = types.str;
+ description = "name of the virtualhost";
+ };
+ aliases = mkOption {
+ type = types.listOf types.str;
+ description = "aliases of the virtualhost";
+ default = [];
+ };
+ webapps = mkOption {
+ type = types.listOf types.path;
+ description = ''
+ List containing web application WAR files and/or directories containing
+ web applications and configuration files for the virtual host.
+ '';
+ default = [];
+ };
+ };
+ });
+ default = [];
+ description = "List consisting of a virtual host name and a list of web applications to deploy on each virtual host";
+ };
+
+ logPerVirtualHost = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Whether to enable logging per virtual host.";
+ };
+
+ jdk = mkOption {
+ type = types.package;
+ default = pkgs.jdk;
+ defaultText = "pkgs.jdk";
+ description = "Which JDK to use.";
+ };
+
+ axis2 = {
+
+ enable = mkOption {
+ default = false;
+ type = types.bool;
+ description = "Whether to enable an Apache Axis2 container";
+ };
+
+ services = mkOption {
+ default = [];
+ type = types.listOf types.str;
+ description = "List containing AAR files or directories with AAR files which are web services to be deployed on Axis2";
+ };
+
+ };
+
+ };
+
+ };
+
+
+ ###### implementation
+
+ config = mkIf config.services.tomcat.enable {
+
+ users.groups = singleton
+ { name = "tomcat";
+ gid = config.ids.gids.tomcat;
+ };
+
+ users.users = singleton
+ { name = "tomcat";
+ uid = config.ids.uids.tomcat;
+ description = "Tomcat user";
+ home = "/homeless-shelter";
+ extraGroups = cfg.extraGroups;
+ };
+
+ systemd.services.tomcat = {
+ description = "Apache Tomcat server";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.target" ];
+
+ preStart = ''
+ ${lib.optionalString cfg.purifyOnStart ''
+ # Delete most directories/symlinks we create from the existing base directory,
+ # to get rid of remainders of an old configuration.
+ # The list of directories to delete is taken from the "mkdir" command below,
+ # excluding "logs" (because logs are valuable) and "work" (because normally
+ # session files are there), and additionally including "bin".
+ rm -rf ${cfg.baseDir}/{conf,virtualhosts,temp,lib,shared/lib,webapps,bin}
+ ''}
+
+ # Create the base directory
+ mkdir -p \
+ ${cfg.baseDir}/{conf,virtualhosts,logs,temp,lib,shared/lib,webapps,work}
+ chown ${cfg.user}:${cfg.group} \
+ ${cfg.baseDir}/{conf,virtualhosts,logs,temp,lib,shared/lib,webapps,work}
+
+ # Create a symlink to the bin directory of the tomcat component
+ ln -sfn ${tomcat}/bin ${cfg.baseDir}/bin
+
+ # Symlink the config files in the conf/ directory (except for catalina.properties and server.xml)
+ for i in $(ls ${tomcat}/conf | grep -v catalina.properties | grep -v server.xml); do
+ ln -sfn ${tomcat}/conf/$i ${cfg.baseDir}/conf/`basename $i`
+ done
+
+ ${if cfg.extraConfigFiles != [] then ''
+ for i in ${toString cfg.extraConfigFiles}; do
+ ln -sfn $i ${cfg.baseDir}/conf/`basename $i`
+ done
+ '' else ""}
+
+ # Create a modified catalina.properties file
+ # Change all references from CATALINA_HOME to CATALINA_BASE and add support for shared libraries
+ sed -e 's|''${catalina.home}|''${catalina.base}|g' \
+ -e 's|shared.loader=|shared.loader=''${catalina.base}/shared/lib/*.jar|' \
+ ${tomcat}/conf/catalina.properties > ${cfg.baseDir}/conf/catalina.properties
+
+ ${if cfg.serverXml != "" then ''
+ cp -f ${pkgs.writeTextDir "server.xml" cfg.serverXml}/* ${cfg.baseDir}/conf/
+ '' else
+ let
+ hostElementForVirtualHost = virtualHost: ''
+ <Host name="${virtualHost.name}" appBase="virtualhosts/${virtualHost.name}/webapps"
+ unpackWARs="true" autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false">
+ '' + concatStrings (innerElementsForVirtualHost virtualHost) + ''
+ </Host>
+ '';
+ innerElementsForVirtualHost = virtualHost:
+ (map (alias: ''
+ <Alias>${alias}</Alias>
+ '') virtualHost.aliases)
+ ++ (optional cfg.logPerVirtualHost ''
+ <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs/${virtualHost.name}"
+ prefix="${virtualHost.name}_access_log." pattern="combined" resolveHosts="false"/>
+ '');
+ hostElementsString = concatMapStringsSep "\n" hostElementForVirtualHost cfg.virtualHosts;
+ hostElementsSedString = replaceStrings ["\n"] ["\\\n"] hostElementsString;
+ in ''
+ # Create a modified server.xml which also includes all virtual hosts
+ sed -e "/<Engine name=\"Catalina\" defaultHost=\"localhost\">/a\\"${escapeShellArg hostElementsSedString} \
+ ${tomcat}/conf/server.xml > ${cfg.baseDir}/conf/server.xml
+ ''
+ }
+ ${optionalString (cfg.logDirs != []) ''
+ for i in ${toString cfg.logDirs}; do
+ mkdir -p ${cfg.baseDir}/logs/$i
+ chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs/$i
+ done
+ ''}
+ ${optionalString cfg.logPerVirtualHost (toString (map (h: ''
+ mkdir -p ${cfg.baseDir}/logs/${h.name}
+ chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs/${h.name}
+ '') cfg.virtualHosts))}
+
+ # Symlink all the given common libs files or paths into the lib/ directory
+ for i in ${tomcat} ${toString cfg.commonLibs}; do
+ if [ -f $i ]; then
+ # If the given web application is a file, symlink it into the common/lib/ directory
+ ln -sfn $i ${cfg.baseDir}/lib/`basename $i`
+ elif [ -d $i ]; then
+ # If the given web application is a directory, then iterate over the files
+ # in the special purpose directories and symlink them into the tomcat tree
+
+ for j in $i/lib/*; do
+ ln -sfn $j ${cfg.baseDir}/lib/`basename $j`
+ done
+ fi
+ done
+
+ # Symlink all the given shared libs files or paths into the shared/lib/ directory
+ for i in ${toString cfg.sharedLibs}; do
+ if [ -f $i ]; then
+ # If the given web application is a file, symlink it into the common/lib/ directory
+ ln -sfn $i ${cfg.baseDir}/shared/lib/`basename $i`
+ elif [ -d $i ]; then
+ # If the given web application is a directory, then iterate over the files
+ # in the special purpose directories and symlink them into the tomcat tree
+
+ for j in $i/shared/lib/*; do
+ ln -sfn $j ${cfg.baseDir}/shared/lib/`basename $j`
+ done
+ fi
+ done
+
+ # Symlink all the given web applications files or paths into the webapps/ directory
+ for i in ${toString cfg.webapps}; do
+ if [ -f $i ]; then
+ # If the given web application is a file, symlink it into the webapps/ directory
+ ln -sfn $i ${cfg.baseDir}/webapps/`basename $i`
+ elif [ -d $i ]; then
+ # If the given web application is a directory, then iterate over the files
+ # in the special purpose directories and symlink them into the tomcat tree
+
+ for j in $i/webapps/*; do
+ ln -sfn $j ${cfg.baseDir}/webapps/`basename $j`
+ done
+
+ # Also symlink the configuration files if they are included
+ if [ -d $i/conf/Catalina ]; then
+ for j in $i/conf/Catalina/*; do
+ mkdir -p ${cfg.baseDir}/conf/Catalina/localhost
+ ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j`
+ done
+ fi
+ fi
+ done
+
+ ${toString (map (virtualHost: ''
+ # Create webapps directory for the virtual host
+ mkdir -p ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps
+
+ # Modify ownership
+ chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps
+
+ # Symlink all the given web applications files or paths into the webapps/ directory
+ # of this virtual host
+ for i in "${if virtualHost ? webapps then toString virtualHost.webapps else ""}"; do
+ if [ -f $i ]; then
+ # If the given web application is a file, symlink it into the webapps/ directory
+ ln -sfn $i ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $i`
+ elif [ -d $i ]; then
+ # If the given web application is a directory, then iterate over the files
+ # in the special purpose directories and symlink them into the tomcat tree
+
+ for j in $i/webapps/*; do
+ ln -sfn $j ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $j`
+ done
+
+ # Also symlink the configuration files if they are included
+ if [ -d $i/conf/Catalina ]; then
+ for j in $i/conf/Catalina/*; do
+ mkdir -p ${cfg.baseDir}/conf/Catalina/${virtualHost.name}
+ ln -sfn $j ${cfg.baseDir}/conf/Catalina/${virtualHost.name}/`basename $j`
+ done
+ fi
+ fi
+ done
+ '') cfg.virtualHosts)}
+
+ ${optionalString cfg.axis2.enable ''
+ # Copy the Axis2 web application
+ cp -av ${pkgs.axis2}/webapps/axis2 ${cfg.baseDir}/webapps
+
+ # Turn off addressing, which causes many errors
+ sed -i -e 's%<module ref="addressing"/>%<!-- <module ref="addressing"/> -->%' ${cfg.baseDir}/webapps/axis2/WEB-INF/conf/axis2.xml
+
+ # Modify permissions on the Axis2 application
+ chown -R ${cfg.user}:${cfg.group} ${cfg.baseDir}/webapps/axis2
+
+ # Symlink all the given web service files or paths into the webapps/axis2/WEB-INF/services directory
+ for i in ${toString cfg.axis2.services}; do
+ if [ -f $i ]; then
+ # If the given web service is a file, symlink it into the webapps/axis2/WEB-INF/services
+ ln -sfn $i ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $i`
+ elif [ -d $i ]; then
+ # If the given web application is a directory, then iterate over the files
+ # in the special purpose directories and symlink them into the tomcat tree
+
+ for j in $i/webapps/axis2/WEB-INF/services/*; do
+ ln -sfn $j ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $j`
+ done
+
+ # Also symlink the configuration files if they are included
+ if [ -d $i/conf/Catalina ]; then
+ for j in $i/conf/Catalina/*; do
+ ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j`
+ done
+ fi
+ fi
+ done
+ ''}
+ '';
+
+ serviceConfig = {
+ Type = "forking";
+ PermissionsStartOnly = true;
+ PIDFile="/run/tomcat/tomcat.pid";
+ RuntimeDirectory = "tomcat";
+ User = cfg.user;
+ Environment=[
+ "CATALINA_BASE=${cfg.baseDir}"
+ "CATALINA_PID=/run/tomcat/tomcat.pid"
+ "JAVA_HOME='${cfg.jdk}'"
+ "JAVA_OPTS='${builtins.toString cfg.javaOpts}'"
+ "CATALINA_OPTS='${builtins.toString cfg.catalinaOpts}'"
+ ] ++ cfg.extraEnvironment;
+ ExecStart = "${tomcat}/bin/startup.sh";
+ ExecStop = "${tomcat}/bin/shutdown.sh";
+ };
+ };
+ };
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/traefik.nix b/nixpkgs/nixos/modules/services/web-servers/traefik.nix
new file mode 100644
index 00000000000..8de7df0d446
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/traefik.nix
@@ -0,0 +1,124 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.services.traefik;
+ configFile =
+ if cfg.configFile == null then
+ pkgs.runCommand "config.toml" {
+ buildInputs = [ pkgs.remarshal ];
+ preferLocalBuild = true;
+ } ''
+ remarshal -if json -of toml \
+ < ${pkgs.writeText "config.json" (builtins.toJSON cfg.configOptions)} \
+ > $out
+ ''
+ else cfg.configFile;
+
+in {
+ options.services.traefik = {
+ enable = mkEnableOption "Traefik web server";
+
+ configFile = mkOption {
+ default = null;
+ example = literalExample "/path/to/config.toml";
+ type = types.nullOr types.path;
+ description = ''
+ Path to verbatim traefik.toml to use.
+ (Using that option has precedence over <literal>configOptions</literal>)
+ '';
+ };
+
+ configOptions = mkOption {
+ description = ''
+ Config for Traefik.
+ '';
+ type = types.attrs;
+ default = {
+ defaultEntryPoints = ["http"];
+ entryPoints.http.address = ":80";
+ };
+ example = {
+ defaultEntrypoints = [ "http" ];
+ web.address = ":8080";
+ entryPoints.http.address = ":80";
+
+ file = {};
+ frontends = {
+ frontend1 = {
+ backend = "backend1";
+ routes.test_1.rule = "Host:localhost";
+ };
+ };
+ backends.backend1 = {
+ servers.server1.url = "http://localhost:8000";
+ };
+ };
+ };
+
+ dataDir = mkOption {
+ default = "/var/lib/traefik";
+ type = types.path;
+ description = ''
+ Location for any persistent data traefik creates, ie. acme
+ '';
+ };
+
+ group = mkOption {
+ default = "traefik";
+ type = types.str;
+ example = "docker";
+ description = ''
+ Set the group that traefik runs under.
+ For the docker backend this needs to be set to <literal>docker</literal> instead.
+ '';
+ };
+
+ package = mkOption {
+ default = pkgs.traefik;
+ defaultText = "pkgs.traefik";
+ type = types.package;
+ description = "Traefik package to use.";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ systemd.tmpfiles.rules = [
+ "d '${cfg.dataDir}' 0700 traefik traefik - -"
+ ];
+
+ systemd.services.traefik = {
+ description = "Traefik web server";
+ after = [ "network-online.target" ];
+ wantedBy = [ "multi-user.target" ];
+ serviceConfig = {
+ ExecStart = ''${cfg.package.bin}/bin/traefik --configfile=${configFile}'';
+ Type = "simple";
+ User = "traefik";
+ Group = cfg.group;
+ Restart = "on-failure";
+ StartLimitInterval = 86400;
+ StartLimitBurst = 5;
+ AmbientCapabilities = "cap_net_bind_service";
+ CapabilityBoundingSet = "cap_net_bind_service";
+ NoNewPrivileges = true;
+ LimitNPROC = 64;
+ LimitNOFILE = 1048576;
+ PrivateTmp = true;
+ PrivateDevices = true;
+ ProtectHome = true;
+ ProtectSystem = "full";
+ ReadWriteDirectories = cfg.dataDir;
+ };
+ };
+
+ users.users.traefik = {
+ group = "traefik";
+ home = cfg.dataDir;
+ createHome = true;
+ };
+
+ users.groups.traefik = {};
+ };
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/unit/default.nix b/nixpkgs/nixos/modules/services/web-servers/unit/default.nix
new file mode 100644
index 00000000000..a4a9d370d64
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/unit/default.nix
@@ -0,0 +1,125 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.services.unit;
+
+ configFile = pkgs.writeText "unit.json" cfg.config;
+
+in {
+ options = {
+ services.unit = {
+ enable = mkEnableOption "Unit App Server";
+ package = mkOption {
+ type = types.package;
+ default = pkgs.unit;
+ defaultText = "pkgs.unit";
+ description = "Unit package to use.";
+ };
+ user = mkOption {
+ type = types.str;
+ default = "unit";
+ description = "User account under which unit runs.";
+ };
+ group = mkOption {
+ type = types.str;
+ default = "unit";
+ description = "Group account under which unit runs.";
+ };
+ stateDir = mkOption {
+ default = "/var/spool/unit";
+ description = "Unit data directory.";
+ };
+ logDir = mkOption {
+ default = "/var/log/unit";
+ description = "Unit log directory.";
+ };
+ config = mkOption {
+ type = types.str;
+ default = ''
+ {
+ "listeners": {},
+ "applications": {}
+ }
+ '';
+ example = literalExample ''
+ {
+ "listeners": {
+ "*:8300": {
+ "application": "example-php-72"
+ }
+ },
+ "applications": {
+ "example-php-72": {
+ "type": "php 7.2",
+ "processes": 4,
+ "user": "nginx",
+ "group": "nginx",
+ "root": "/var/www",
+ "index": "index.php",
+ "options": {
+ "file": "/etc/php.d/default.ini",
+ "admin": {
+ "max_execution_time": "30",
+ "max_input_time": "30",
+ "display_errors": "off",
+ "display_startup_errors": "off",
+ "open_basedir": "/dev/urandom:/proc/cpuinfo:/proc/meminfo:/etc/ssl/certs:/var/www",
+ "disable_functions": "exec,passthru,shell_exec,system"
+ }
+ }
+ }
+ }
+ }
+ '';
+ description = "Unit configuration in JSON format. More details here https://unit.nginx.org/configuration";
+ };
+ };
+ };
+
+ config = mkIf cfg.enable {
+
+ environment.systemPackages = [ cfg.package ];
+
+ systemd.tmpfiles.rules = [
+ "d '${cfg.stateDir}' 0750 ${cfg.user} ${cfg.group} - -"
+ "d '${cfg.logDir}' 0750 ${cfg.user} ${cfg.group} - -"
+ ];
+
+ systemd.services.unit = {
+ description = "Unit App Server";
+ after = [ "network.target" ];
+ wantedBy = [ "multi-user.target" ];
+ path = with pkgs; [ curl ];
+ preStart = ''
+ test -f '/run/unit/control.unit.sock' || rm -f '/run/unit/control.unit.sock'
+ '';
+ postStart = ''
+ curl -X PUT --data-binary '@${configFile}' --unix-socket '/run/unit/control.unit.sock' 'http://localhost/config'
+ '';
+ serviceConfig = {
+ User = cfg.user;
+ Group = cfg.group;
+ AmbientCapabilities = "CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID";
+ CapabilityBoundingSet = "CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID";
+ ExecStart = ''
+ ${cfg.package}/bin/unitd --control 'unix:/run/unit/control.unit.sock' --pid '/run/unit/unit.pid' \
+ --log '${cfg.logDir}/unit.log' --state '${cfg.stateDir}' --no-daemon \
+ --user ${cfg.user} --group ${cfg.group}
+ '';
+ RuntimeDirectory = "unit";
+ RuntimeDirectoryMode = "0750";
+ };
+ };
+
+ users.users = optionalAttrs (cfg.user == "unit") (singleton {
+ name = "unit";
+ group = cfg.group;
+ });
+
+ users.groups = optionalAttrs (cfg.group == "unit") (singleton {
+ name = "unit";
+ });
+ };
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/uwsgi.nix b/nixpkgs/nixos/modules/services/web-servers/uwsgi.nix
new file mode 100644
index 00000000000..af70f32f32d
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/uwsgi.nix
@@ -0,0 +1,160 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.services.uwsgi;
+
+ uwsgi = pkgs.uwsgi.override {
+ plugins = cfg.plugins;
+ };
+
+ buildCfg = name: c:
+ let
+ plugins =
+ if any (n: !any (m: m == n) cfg.plugins) (c.plugins or [])
+ then throw "`plugins` attribute in UWSGI configuration contains plugins not in config.services.uwsgi.plugins"
+ else c.plugins or cfg.plugins;
+
+ hasPython = v: filter (n: n == "python${v}") plugins != [];
+ hasPython2 = hasPython "2";
+ hasPython3 = hasPython "3";
+
+ python =
+ if hasPython2 && hasPython3 then
+ throw "`plugins` attribute in UWSGI configuration shouldn't contain both python2 and python3"
+ else if hasPython2 then uwsgi.python2
+ else if hasPython3 then uwsgi.python3
+ else null;
+
+ pythonEnv = python.withPackages (c.pythonPackages or (self: []));
+
+ uwsgiCfg = {
+ uwsgi =
+ if c.type == "normal"
+ then {
+ inherit plugins;
+ } // removeAttrs c [ "type" "pythonPackages" ]
+ // optionalAttrs (python != null) {
+ pythonpath = "${pythonEnv}/${python.sitePackages}";
+ env =
+ # Argh, uwsgi expects list of key-values there instead of a dictionary.
+ let env' = c.env or [];
+ getPath =
+ x: if hasPrefix "PATH=" x
+ then substring (stringLength "PATH=") (stringLength x) x
+ else null;
+ oldPaths = filter (x: x != null) (map getPath env');
+ in env' ++ [ "PATH=${optionalString (oldPaths != []) "${last oldPaths}:"}${pythonEnv}/bin" ];
+ }
+ else if c.type == "emperor"
+ then {
+ emperor = if builtins.typeOf c.vassals != "set" then c.vassals
+ else pkgs.buildEnv {
+ name = "vassals";
+ paths = mapAttrsToList buildCfg c.vassals;
+ };
+ } // removeAttrs c [ "type" "vassals" ]
+ else throw "`type` attribute in UWSGI configuration should be either 'normal' or 'emperor'";
+ };
+
+ in pkgs.writeTextDir "${name}.json" (builtins.toJSON uwsgiCfg);
+
+in {
+
+ options = {
+ services.uwsgi = {
+
+ enable = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Enable uWSGI";
+ };
+
+ runDir = mkOption {
+ type = types.path;
+ default = "/run/uwsgi";
+ description = "Where uWSGI communication sockets can live";
+ };
+
+ instance = mkOption {
+ type = types.attrs;
+ default = {
+ type = "normal";
+ };
+ example = literalExample ''
+ {
+ type = "emperor";
+ vassals = {
+ moin = {
+ type = "normal";
+ pythonPackages = self: with self; [ moinmoin ];
+ socket = "${config.services.uwsgi.runDir}/uwsgi.sock";
+ };
+ };
+ }
+ '';
+ description = ''
+ uWSGI configuration. It awaits an attribute <literal>type</literal> inside which can be either
+ <literal>normal</literal> or <literal>emperor</literal>.
+
+ For <literal>normal</literal> mode you can specify <literal>pythonPackages</literal> as a function
+ from libraries set into a list of libraries. <literal>pythonpath</literal> will be set accordingly.
+
+ For <literal>emperor</literal> mode, you should use <literal>vassals</literal> attribute
+ which should be either a set of names and configurations or a path to a directory.
+
+ Other attributes will be used in configuration file as-is. Notice that you can redefine
+ <literal>plugins</literal> setting here.
+ '';
+ };
+
+ plugins = mkOption {
+ type = types.listOf types.str;
+ default = [];
+ description = "Plugins used with uWSGI";
+ };
+
+ user = mkOption {
+ type = types.str;
+ default = "uwsgi";
+ description = "User account under which uwsgi runs.";
+ };
+
+ group = mkOption {
+ type = types.str;
+ default = "uwsgi";
+ description = "Group account under which uwsgi runs.";
+ };
+ };
+ };
+
+ config = mkIf cfg.enable {
+ systemd.services.uwsgi = {
+ wantedBy = [ "multi-user.target" ];
+ preStart = ''
+ mkdir -p ${cfg.runDir}
+ chown ${cfg.user}:${cfg.group} ${cfg.runDir}
+ '';
+ serviceConfig = {
+ Type = "notify";
+ ExecStart = "${uwsgi}/bin/uwsgi --uid ${cfg.user} --gid ${cfg.group} --json ${buildCfg "server" cfg.instance}/server.json";
+ ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+ ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID";
+ NotifyAccess = "main";
+ KillSignal = "SIGQUIT";
+ };
+ };
+
+ users.users = optionalAttrs (cfg.user == "uwsgi") (singleton
+ { name = "uwsgi";
+ group = cfg.group;
+ uid = config.ids.uids.uwsgi;
+ });
+
+ users.groups = optionalAttrs (cfg.group == "uwsgi") (singleton
+ { name = "uwsgi";
+ gid = config.ids.gids.uwsgi;
+ });
+ };
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/varnish/default.nix b/nixpkgs/nixos/modules/services/web-servers/varnish/default.nix
new file mode 100644
index 00000000000..63f967185c2
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/varnish/default.nix
@@ -0,0 +1,113 @@
+{ config, lib, pkgs, ...}:
+
+with lib;
+
+let
+ cfg = config.services.varnish;
+
+ commandLine = "-f ${pkgs.writeText "default.vcl" cfg.config}" +
+ optionalString (cfg.extraModules != []) " -p vmod_path='${makeSearchPathOutput "lib" "lib/varnish/vmods" ([cfg.package] ++ cfg.extraModules)}' -r vmod_path";
+in
+{
+ options = {
+ services.varnish = {
+ enable = mkEnableOption "Varnish Server";
+
+ package = mkOption {
+ type = types.package;
+ default = pkgs.varnish5;
+ defaultText = "pkgs.varnish5";
+ description = ''
+ The package to use
+ '';
+ };
+
+ http_address = mkOption {
+ type = types.str;
+ default = "*:6081";
+ description = "
+ HTTP listen address and port.
+ ";
+ };
+
+ config = mkOption {
+ type = types.lines;
+ description = "
+ Verbatim default.vcl configuration.
+ ";
+ };
+
+ stateDir = mkOption {
+ type = types.path;
+ default = "/var/spool/varnish/${config.networking.hostName}";
+ description = "
+ Directory holding all state for Varnish to run.
+ ";
+ };
+
+ extraModules = mkOption {
+ type = types.listOf types.package;
+ default = [];
+ example = literalExample "[ pkgs.varnish5Packages.geoip ]";
+ description = "
+ Varnish modules (except 'std').
+ ";
+ };
+
+ extraCommandLine = mkOption {
+ type = types.str;
+ default = "";
+ example = "-s malloc,256M";
+ description = "
+ Command line switches for varnishd (run 'varnishd -?' to get list of options)
+ ";
+ };
+ };
+
+ };
+
+ config = mkIf cfg.enable {
+
+ systemd.services.varnish = {
+ description = "Varnish";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.target" ];
+ preStart = ''
+ mkdir -p ${cfg.stateDir}
+ chown -R varnish:varnish ${cfg.stateDir}
+ '';
+ postStop = ''
+ rm -rf ${cfg.stateDir}
+ '';
+ serviceConfig = {
+ Type = "simple";
+ PermissionsStartOnly = true;
+ ExecStart = "${cfg.package}/sbin/varnishd -a ${cfg.http_address} -n ${cfg.stateDir} -F ${cfg.extraCommandLine} ${commandLine}";
+ Restart = "always";
+ RestartSec = "5s";
+ User = "varnish";
+ Group = "varnish";
+ AmbientCapabilities = "cap_net_bind_service";
+ NoNewPrivileges = true;
+ LimitNOFILE = 131072;
+ };
+ };
+
+ environment.systemPackages = [ cfg.package ];
+
+ # check .vcl syntax at compile time (e.g. before nixops deployment)
+ system.extraDependencies = [
+ (pkgs.stdenv.mkDerivation {
+ name = "check-varnish-syntax";
+ buildCommand = "${cfg.package}/sbin/varnishd -C ${commandLine} 2> $out || (cat $out; exit 1)";
+ })
+ ];
+
+ users.users.varnish = {
+ group = "varnish";
+ uid = config.ids.uids.varnish;
+ };
+
+ users.groups.varnish.gid = config.ids.uids.varnish;
+ };
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/zope2.nix b/nixpkgs/nixos/modules/services/web-servers/zope2.nix
new file mode 100644
index 00000000000..3abd506827c
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/zope2.nix
@@ -0,0 +1,258 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.services.zope2;
+
+ zope2Opts = { name, ... }: {
+ options = {
+
+ name = mkOption {
+ default = "${name}";
+ type = types.str;
+ description = "The name of the zope2 instance. If undefined, the name of the attribute set will be used.";
+ };
+
+ threads = mkOption {
+ default = 2;
+ type = types.int;
+ description = "Specify the number of threads that Zope's ZServer web server will use to service requests. ";
+ };
+
+ http_address = mkOption {
+ default = "localhost:8080";
+ type = types.str;
+ description = "Give a port and address for the HTTP server.";
+ };
+
+ user = mkOption {
+ default = "zope2";
+ type = types.str;
+ description = "The name of the effective user for the Zope process.";
+ };
+
+ clientHome = mkOption {
+ default = "/var/lib/zope2/${name}";
+ type = types.path;
+ description = "Home directory of zope2 instance.";
+ };
+ extra = mkOption {
+ default =
+ ''
+ <zodb_db main>
+ mount-point /
+ cache-size 30000
+ <blobstorage>
+ blob-dir /var/lib/zope2/${name}/blobstorage
+ <filestorage>
+ path /var/lib/zope2/${name}/filestorage/Data.fs
+ </filestorage>
+ </blobstorage>
+ </zodb_db>
+ '';
+ type = types.lines;
+ description = "Extra zope.conf";
+ };
+
+ packages = mkOption {
+ type = types.listOf types.package;
+ description = "The list of packages you want to make available to the zope2 instance.";
+ };
+
+ };
+ };
+
+in
+
+{
+
+ ###### interface
+
+ options = {
+
+ services.zope2.instances = mkOption {
+ default = {};
+ type = with types; attrsOf (submodule zope2Opts);
+ example = literalExample ''
+ {
+ plone01 = {
+ http_address = "127.0.0.1:8080";
+ extra =
+ '''
+ <zodb_db main>
+ mount-point /
+ cache-size 30000
+ <blobstorage>
+ blob-dir /var/lib/zope2/plone01/blobstorage
+ <filestorage>
+ path /var/lib/zope2/plone01/filestorage/Data.fs
+ </filestorage>
+ </blobstorage>
+ </zodb_db>
+ ''';
+ };
+ }
+ '';
+ description = "zope2 instances to be created automaticaly by the system.";
+ };
+ };
+
+ ###### implementation
+
+ config = mkIf (cfg.instances != {}) {
+
+ users.users.zope2.uid = config.ids.uids.zope2;
+
+ systemd.services =
+ let
+
+ createZope2Instance = opts: name:
+ let
+ interpreter = pkgs.writeScript "interpreter"
+ ''
+ import sys
+
+ _interactive = True
+ if len(sys.argv) > 1:
+ _options, _args = __import__("getopt").getopt(sys.argv[1:], 'ic:m:')
+ _interactive = False
+ for (_opt, _val) in _options:
+ if _opt == '-i':
+ _interactive = True
+ elif _opt == '-c':
+ exec _val
+ elif _opt == '-m':
+ sys.argv[1:] = _args
+ _args = []
+ __import__("runpy").run_module(
+ _val, {}, "__main__", alter_sys=True)
+
+ if _args:
+ sys.argv[:] = _args
+ __file__ = _args[0]
+ del _options, _args
+ execfile(__file__)
+
+ if _interactive:
+ del _interactive
+ __import__("code").interact(banner="", local=globals())
+ '';
+ env = pkgs.buildEnv {
+ name = "zope2-${name}-env";
+ paths = [
+ pkgs.python27
+ pkgs.python27Packages.recursivePthLoader
+ pkgs.python27Packages."plone.recipe.zope2instance"
+ ] ++ attrValues pkgs.python27.modules
+ ++ opts.packages;
+ postBuild =
+ ''
+ echo "#!$out/bin/python" > $out/bin/interpreter
+ cat ${interpreter} >> $out/bin/interpreter
+ '';
+ };
+ conf = pkgs.writeText "zope2-${name}-conf"
+ ''
+ %define INSTANCEHOME ${env}
+ instancehome $INSTANCEHOME
+ %define CLIENTHOME ${opts.clientHome}/${opts.name}
+ clienthome $CLIENTHOME
+
+ debug-mode off
+ security-policy-implementation C
+ verbose-security off
+ default-zpublisher-encoding utf-8
+ zserver-threads ${toString opts.threads}
+ effective-user ${opts.user}
+
+ pid-filename ${opts.clientHome}/${opts.name}/pid
+ lock-filename ${opts.clientHome}/${opts.name}/lock
+ python-check-interval 1000
+ enable-product-installation off
+
+ <environment>
+ zope_i18n_compile_mo_files false
+ </environment>
+
+ <eventlog>
+ level INFO
+ <logfile>
+ path /var/log/zope2/${name}.log
+ level INFO
+ </logfile>
+ </eventlog>
+
+ <logger access>
+ level WARN
+ <logfile>
+ path /var/log/zope2/${name}-Z2.log
+ format %(message)s
+ </logfile>
+ </logger>
+
+ <http-server>
+ address ${opts.http_address}
+ </http-server>
+
+ <zodb_db temporary>
+ <temporarystorage>
+ name temporary storage for sessioning
+ </temporarystorage>
+ mount-point /temp_folder
+ container-class Products.TemporaryFolder.TemporaryContainer
+ </zodb_db>
+
+ ${opts.extra}
+ '';
+ ctlScript = pkgs.writeScript "zope2-${name}-ctl-script"
+ ''
+ #!${env}/bin/python
+
+ import sys
+ import plone.recipe.zope2instance.ctl
+
+ if __name__ == '__main__':
+ sys.exit(plone.recipe.zope2instance.ctl.main(
+ ["-C", "${conf}"]
+ + sys.argv[1:]))
+ '';
+
+ ctl = pkgs.writeScript "zope2-${name}-ctl"
+ ''
+ #!${pkgs.bash}/bin/bash -e
+ export PYTHONHOME=${env}
+ exec ${ctlScript} "$@"
+ '';
+ in {
+ #description = "${name} instance";
+ after = [ "network.target" ]; # with RelStorage also add "postgresql.service"
+ wantedBy = [ "multi-user.target" ];
+ path = opts.packages;
+ preStart =
+ ''
+ mkdir -p /var/log/zope2/
+ touch /var/log/zope2/${name}.log
+ touch /var/log/zope2/${name}-Z2.log
+ chown ${opts.user} /var/log/zope2/${name}.log
+ chown ${opts.user} /var/log/zope2/${name}-Z2.log
+
+ mkdir -p ${opts.clientHome}/filestorage ${opts.clientHome}/blobstorage
+ mkdir -p ${opts.clientHome}/${opts.name}
+ chown ${opts.user} ${opts.clientHome} -R
+
+ ${ctl} adduser admin admin
+ '';
+
+ serviceConfig.Type = "forking";
+ serviceConfig.ExecStart = "${ctl} start";
+ serviceConfig.ExecStop = "${ctl} stop";
+ serviceConfig.ExecReload = "${ctl} restart";
+ };
+
+ in listToAttrs (map (name: { name = "zope2-${name}"; value = createZope2Instance (builtins.getAttr name cfg.instances) name; }) (builtins.attrNames cfg.instances));
+
+ };
+
+}