diff options
Diffstat (limited to 'nixos/modules/services/web-servers')
16 files changed, 2693 insertions, 0 deletions
diff --git a/nixos/modules/services/web-servers/apache-httpd/default.nix b/nixos/modules/services/web-servers/apache-httpd/default.nix new file mode 100644 index 0000000000000..e2cfc5cdb2656 --- /dev/null +++ b/nixos/modules/services/web-servers/apache-httpd/default.nix @@ -0,0 +1,662 @@ +{ config, pkgs, ... }: + +with pkgs.lib; + +let + + mainCfg = config.services.httpd; + + httpd = mainCfg.package; + + version24 = !versionOlder httpd.version "2.4"; + + httpdConf = mainCfg.configFile; + + php = pkgs.php.override { apacheHttpd = httpd; }; + + getPort = cfg: if cfg.port != 0 then cfg.port else if cfg.enableSSL then 443 else 80; + + extraModules = attrByPath ["extraModules"] [] mainCfg; + extraForeignModules = filter builtins.isAttrs extraModules; + extraApacheModules = filter (x: !(builtins.isAttrs x)) extraModules; # I'd prefer using builtins.isString here, but doesn't exist yet + + + makeServerInfo = cfg: { + # Canonical name must not include a trailing slash. + canonicalName = + (if cfg.enableSSL then "https" else "http") + "://" + + cfg.hostName + + (if getPort cfg != (if cfg.enableSSL then 443 else 80) then ":${toString (getPort cfg)}" else ""); + + # Admin address: inherit from the main server if not specified for + # a virtual host. + adminAddr = if cfg.adminAddr != "" then cfg.adminAddr else mainCfg.adminAddr; + + vhostConfig = cfg; + serverConfig = mainCfg; + fullConfig = config; # machine config + }; + + + vhostOptions = import ./per-server-options.nix { + inherit mkOption; + forMainServer = false; + }; + + vhosts = let + makeVirtualHost = cfgIn: + let + # Fill in defaults for missing options. + cfg = addDefaultOptionValues vhostOptions cfgIn; + in cfg; + in map makeVirtualHost mainCfg.virtualHosts; + + + allHosts = [mainCfg] ++ vhosts; + + + callSubservices = serverInfo: defs: + let f = svc: + let + svcFunction = + if svc ? function then svc.function + else import "${./.}/${if svc ? serviceType then svc.serviceType else svc.serviceName}.nix"; + config = addDefaultOptionValues res.options + (if svc ? config then svc.config else svc); + defaults = { + extraConfig = ""; + extraModules = []; + extraModulesPre = []; + extraPath = []; + extraServerPath = []; + globalEnvVars = []; + robotsEntries = ""; + startupScript = ""; + enablePHP = false; + phpOptions = ""; + options = {}; + }; + res = defaults // svcFunction { inherit config 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 vhosts; + + + # !!! should be in lib + writeTextInDir = name: text: + pkgs.runCommand name {inherit text;} "ensureDir $out; echo -n \"$text\" > $out/$name"; + + + 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" + (if version24 then "authn_core" else "authn_alias") + + # Authorization: is the user allowed access? + "authz_user" "authz_groupfile" "authz_host" + + # 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" + ] + ++ optionals version24 [ + "mpm_${mainCfg.multiProcessingModule}" + "authz_core" + "unixd" + ] + ++ (if mainCfg.multiProcessingModule == "prefork" then [ "cgi" ] else [ "cgid" ]) + ++ optional enableSSL "ssl" + ++ extraApacheModules; + + + allDenied = if version24 then '' + Require all denied + '' else '' + Order deny,allow + Deny from all + ''; + + allGranted = if version24 then '' + Require all granted + '' else '' + Order allow,deny + Allow from all + ''; + + + loggingConf = '' + 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} + ''; + + + 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 shm:${mainCfg.stateDir}/ssl_scache(512000) + + SSLMutex posixsem + + SSLRandomSeed startup builtin + SSLRandomSeed connect builtin + ''; + + + 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> + + AddEncoding x-compress Z + AddEncoding x-gzip gz tgz + ''; + + + perServerConf = isMainServer: cfg: let + + serverInfo = makeServerInfo cfg; + + subservices = callSubservices serverInfo cfg.extraSubservices; + + documentRoot = if cfg.documentRoot != null then cfg.documentRoot else + pkgs.runCommand "empty" {} "ensureDir $out"; + + documentRootConf = '' + DocumentRoot "${documentRoot}" + + <Directory "${documentRoot}"> + Options Indexes FollowSymLinks + AllowOverride None + ${allGranted} + </Directory> + ''; + + robotsTxt = pkgs.writeText "robots.txt" '' + ${# If this is a vhost, the include the entries for the main server as well. + if isMainServer then "" + else concatMapStrings (svc: svc.robotsEntries) mainSubservices} + ${concatMapStrings (svc: svc.robotsEntries) subservices} + ''; + + robotsConf = '' + Alias /robots.txt ${robotsTxt} + ''; + + in '' + ServerName ${serverInfo.canonicalName} + + ${concatMapStrings (alias: "ServerAlias ${alias}\n") cfg.serverAliases} + + ${if cfg.sslServerCert != "" then '' + SSLCertificateFile ${cfg.sslServerCert} + SSLCertificateKeyFile ${cfg.sslServerKey} + '' 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 != "" then '' + ServerAdmin ${cfg.adminAddr} + '' else ""} + + ${if !isMainServer && mainCfg.logPerVirtualHost then '' + ErrorLog ${mainCfg.logDir}/error_log-${cfg.hostName} + CustomLog ${mainCfg.logDir}/access_log-${cfg.hostName} ${cfg.logFormat} + '' else ""} + + ${robotsConf} + + ${if isMainServer || cfg.documentRoot != 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 != "" 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} + + ${optionalString version24 '' + 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 + ports = map getPort allHosts; + uniquePorts = uniqList {inputList = ports;}; + in concatMapStrings (port: "Listen ${toString port}\n") uniquePorts + } + + 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 enablePHP { name = "php5"; path = "${php}/modules/libphp5.so"; } + ++ concatMap (svc: svc.extraModules) allSubservices + ++ extraForeignModules; + in concatMapStrings load 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 + + ${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} + + # Always enable virtual hosts; it doesn't seem to hurt. + ${let + ports = map getPort allHosts; + uniquePorts = uniqList {inputList = ports;}; + directives = concatMapStrings (port: "NameVirtualHost *:${toString port}\n") uniquePorts; + in optionalString (!version24) directives + } + + ${let + makeVirtualHost = vhost: '' + <VirtualHost *:${toString (getPort vhost)}> + ${perServerConf false vhost} + </VirtualHost> + ''; + in concatMapStrings makeVirtualHost vhosts + } + ''; + + + enablePHP = any (svc: svc.enablePHP) 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)); + } + '' + cat ${php}/etc/php-recommended.ini > $out + echo "$options" >> $out + ''; + +in + + +{ + + ###### interface + + options = { + + services.httpd = { + + enable = mkOption { + default = false; + description = " + Whether to enable the Apache httpd server. + "; + }; + + package = mkOption { + default = pkgs.apacheHttpd.override { mpm = mainCfg.multiProcessingModule; }; + example = "pkgs.apacheHttpd_2_4"; + description = " + Overridable attribute of the Apache HTTP Server package to use. + "; + }; + + configFile = mkOption { + default = confFile; + example = ''pkgs.writeText "httpd.conf" "# my custom config file ...";''; + description = " + Overridable config file to use for Apache. By default, use the + file automatically generated by nixos. + "; + }; + + extraConfig = mkOption { + default = ""; + description = " + These configuration lines will be appended to the Apache config + file. Note that this mechanism may not work when <option>configFile</option> + is overridden. + "; + }; + + extraModules = mkOption { + default = []; + example = [ "proxy_connect" { name = "php5"; path = "${php}/modules/libphp5.so"; } ]; + description = '' + Specifies additional Apache modules. 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 { + 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 { + default = "wwwrun"; + description = " + User account under which httpd runs. The account is created + automatically if it doesn't exist. + "; + }; + + group = mkOption { + default = "wwwrun"; + description = " + Group under which httpd runs. The account is created + automatically if it doesn't exist. + "; + }; + + logDir = mkOption { + default = "/var/log/httpd"; + description = " + Directory for Apache's log files. It is created automatically. + "; + }; + + stateDir = mkOption { + default = "/var/run/httpd"; + description = " + Directory for Apache's transient runtime state (such as PID + files). It is created automatically. Note that the default, + <filename>/var/run/httpd</filename>, is deleted at boot time. + "; + }; + + virtualHosts = mkOption { + 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. + ''; + }; + + phpOptions = mkOption { + default = ""; + example = + '' + date.timezone = "CET" + ''; + description = + "Options appended to the PHP configuration file <filename>php.ini</filename>."; + }; + + multiProcessingModule = mkOption { + default = "prefork"; + example = "worker"; + type = types.uniq types.string; + 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 { + default = 150; + example = 8; + description = "Maximum number of httpd processes (prefork)"; + }; + + maxRequestsPerChild = mkOption { + default = 0; + example = 500; + description = + "Maximum number of httpd requests answered per httpd child (prefork), 0 means unlimited"; + }; + } + + # Include the options shared between the main server and virtual hosts. + // (import ./per-server-options.nix { + inherit mkOption; + forMainServer = true; + }); + + }; + + + ###### implementation + + config = mkIf config.services.httpd.enable { + + users.extraUsers = optionalAttrs (mainCfg.user == "wwwrun") singleton + { name = "wwwrun"; + group = "wwwrun"; + description = "Apache httpd user"; + uid = config.ids.uids.wwwrun; + }; + + users.extraGroups = 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 + + ; Apparently PHP doesn't use $TZ. + date.timezone = "${config.time.timeZone}" + ''; + + systemd.services.httpd = + { description = "Apache HTTPD"; + + wantedBy = [ "multi-user.target" ]; + requires = [ "keys.target" ]; + after = [ "network.target" "fs.target" "postgresql.service" "keys.target" ]; + + path = + [ httpd pkgs.coreutils pkgs.gnugrep ] + ++ # Needed for PHP's mail() function. !!! Probably the + # ssmtp module should export the path to sendmail in + # some way. + optional config.networking.defaultMailServer.directDelivery pkgs.ssmtp + ++ concatMap (svc: svc.extraServerPath) allSubservices; + + environment = + { PHPRC = if enablePHP then phpIni else ""; + } // (listToAttrs (concatMap (svc: svc.globalEnvVars) allSubservices)); + + preStart = + '' + mkdir -m 0750 -p ${mainCfg.stateDir} + chown root.${mainCfg.group} ${mainCfg.stateDir} + ${optionalString version24 '' + mkdir -m 0750 -p "${mainCfg.stateDir}/runtime" + chown root.${mainCfg.group} "${mainCfg.stateDir}/runtime" + ''} + mkdir -m 0700 -p ${mainCfg.logDir} + + ${optionalString (mainCfg.documentRoot != null) + '' + # Create the document root directory if does not exists yet + mkdir -p ${mainCfg.documentRoot} + '' + } + + # 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.Type = "forking"; + serviceConfig.Restart = "always"; + }; + + }; + +} diff --git a/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix b/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix new file mode 100644 index 0000000000000..dcc05b03891b9 --- /dev/null +++ b/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix @@ -0,0 +1,303 @@ +{ config, pkgs, serverInfo, php, ... }: + +with pkgs.lib; + +let + + mediawikiConfig = pkgs.writeText "LocalSettings.php" + '' + <?php + # Copied verbatim from the default (generated) LocalSettings.php. + if( defined( 'MW_INSTALL_PATH' ) ) { + $IP = MW_INSTALL_PATH; + } else { + $IP = dirname( __FILE__ ); + } + + $path = array( $IP, "$IP/includes", "$IP/languages" ); + set_include_path( implode( PATH_SEPARATOR, $path ) . PATH_SEPARATOR . get_include_path() ); + + require_once( "$IP/includes/DefaultSettings.php" ); + + if ( $wgCommandLineMode ) { + if ( isset( $_SERVER ) && array_key_exists( 'REQUEST_METHOD', $_SERVER ) ) { + die( "This script must be run from the command line\n" ); + } + } + + $wgScriptPath = "${config.urlPrefix}"; + + # We probably need to set $wgSecretKey and $wgCacheEpoch. + + # Paths to external programs. + $wgDiff3 = "${pkgs.diffutils}/bin/diff3"; + $wgDiff = "${pkgs.diffutils}/bin/diff"; + $wgImageMagickConvertCommand = "${pkgs.imagemagick}/bin/convert"; + + #$wgDebugLogFile = "/tmp/mediawiki_debug_log.txt"; + + # Database configuration. + $wgDBtype = "${config.dbType}"; + $wgDBserver = "${config.dbServer}"; + $wgDBuser = "${config.dbUser}"; + $wgDBpassword = "${config.dbPassword}"; + $wgDBname = "${config.dbName}"; + + # E-mail. + $wgEmergencyContact = "${config.emergencyContact}"; + $wgPasswordSender = "${config.passwordSender}"; + + $wgSitename = "${config.siteName}"; + + ${optionalString (config.logo != "") '' + $wgLogo = "${config.logo}"; + ''} + + ${optionalString (config.articleUrlPrefix != "") '' + $wgArticlePath = "${config.articleUrlPrefix}/$1"; + ''} + + ${optionalString config.enableUploads '' + $wgEnableUploads = true; + $wgUploadDirectory = "${config.uploadDir}"; + ''} + + ${optionalString (config.defaultSkin != "") '' + $wgDefaultSkin = "${config.defaultSkin}"; + ''} + + ${config.extraConfig} + ?> + ''; + + # Unpack Mediawiki and put the config file in its root directory. + mediawikiRoot = pkgs.stdenv.mkDerivation rec { + name= "mediawiki-1.20.5"; + + src = pkgs.fetchurl { + url = "http://download.wikimedia.org/mediawiki/1.20/${name}.tar.gz"; + sha256 = "0ix6khrilfdncjqnh41xjs0bd49i1q0rywycjaixjfpwj6vjbqbl"; + }; + + skins = config.skins; + + buildPhase = + '' + for skin in $skins; do + cp -prvd $skin/* skins/ + done + ''; # */ + + installPhase = + '' + ensureDir $out + cp -r * $out + cp ${mediawikiConfig} $out/LocalSettings.php + ''; + }; + + mediawikiScripts = pkgs.runCommand "mediawiki-${config.id}-scripts" + { buildInputs = [ pkgs.makeWrapper ]; } + '' + ensureDir $out/bin + for i in changePassword.php createAndPromote.php userOptions.php edit.php nukePage.php update.php; do + makeWrapper ${php}/bin/php $out/bin/mediawiki-${config.id}-$(basename $i .php) \ + --add-flags ${mediawikiRoot}/maintenance/$i + done + ''; + +in + +{ + + extraConfig = + '' + ${optionalString config.enableUploads '' + Alias ${config.urlPrefix}/images ${config.uploadDir} + + <Directory ${config.uploadDir}> + Order allow,deny + Allow from all + Options -Indexes + </Directory> + ''} + + Alias ${config.urlPrefix} ${mediawikiRoot} + + <Directory ${mediawikiRoot}> + Order allow,deny + Allow from all + DirectoryIndex index.php + </Directory> + + ${optionalString (config.articleUrlPrefix != "") '' + Alias ${config.articleUrlPrefix} ${mediawikiRoot}/index.php + ''} + ''; + + enablePHP = true; + + options = { + + id = mkOption { + default = "main"; + description = '' + A unique identifier necessary to keep multiple MediaWiki server + instances on the same machine apart. This is used to + disambiguate the administrative scripts, which get names like + mediawiki-$id-change-password. + ''; + }; + + dbType = mkOption { + default = "postgres"; + example = "mysql"; + description = "Database type."; + }; + + dbName = mkOption { + default = "mediawiki"; + description = "Name of the database that holds the MediaWiki data."; + }; + + dbServer = mkOption { + default = ""; # use a Unix domain socket + example = "10.0.2.2"; + description = '' + The location of the database server. Leave empty to use a + database server running on the same machine through a Unix + domain socket. + ''; + }; + + dbUser = mkOption { + default = "mediawiki"; + description = "The user name for accessing the database."; + }; + + dbPassword = mkOption { + default = ""; + example = "foobar"; + description = '' + The password of the database user. Warning: this is stored in + cleartext in the Nix store! + ''; + }; + + emergencyContact = mkOption { + default = serverInfo.serverConfig.adminAddr; + example = "admin@example.com"; + description = '' + Emergency contact e-mail address. Defaults to the Apache + admin address. + ''; + }; + + passwordSender = mkOption { + default = serverInfo.serverConfig.adminAddr; + example = "password@example.com"; + description = '' + E-mail address from which password confirmations originate. + Defaults to the Apache admin address. + ''; + }; + + siteName = mkOption { + default = "MediaWiki"; + example = "Foobar Wiki"; + description = "Name of the wiki"; + }; + + logo = mkOption { + default = ""; + example = "/images/logo.png"; + description = "The URL of the site's logo (which should be a 135x135px image)."; + }; + + urlPrefix = mkOption { + default = "/w"; + description = '' + The URL prefix under which the Mediawiki service appears. + ''; + }; + + articleUrlPrefix = mkOption { + default = "/wiki"; + example = ""; + description = '' + The URL prefix under which article pages appear, + e.g. http://server/wiki/Page. Leave empty to use the main URL + prefix, e.g. http://server/w/index.php?title=Page. + ''; + }; + + enableUploads = mkOption { + default = false; + description = "Whether to enable file uploads."; + }; + + uploadDir = mkOption { + default = throw "You must specify `uploadDir'."; + example = "/data/mediawiki-upload"; + description = "The directory that stores uploaded files."; + }; + + defaultSkin = mkOption { + default = ""; + example = "nostalgia"; + description = "Set this value to change the default skin used by MediaWiki."; + }; + + skins = mkOption { + default = []; + type = types.listOf types.path; + description = + '' + List of paths whose content is copied to the ‘skins’ + subdirectory of the MediaWiki installation. + ''; + }; + + extraConfig = mkOption { + default = ""; + example = + '' + $wgEnableEmail = false; + ''; + description = '' + Any additional text to be appended to MediaWiki's + configuration file. This is a PHP script. For configuration + settings, see <link xlink:href='http://www.mediawiki.org/wiki/Manual:Configuration_settings'/>. + ''; + }; + + }; + + extraPath = [ mediawikiScripts ]; + + # !!! Need to specify that Apache has a dependency on PostgreSQL! + + startupScript = pkgs.writeScript "mediawiki_startup.sh" + # Initialise the database automagically if we're using a Postgres + # server on localhost. + (optionalString (config.dbType == "postgres" && config.dbServer == "") '' + if ! ${pkgs.postgresql}/bin/psql -l | grep -q ' ${config.dbName} ' ; then + ${pkgs.postgresql}/bin/createuser --no-superuser --no-createdb --no-createrole "${config.dbUser}" || true + ${pkgs.postgresql}/bin/createdb "${config.dbName}" -O "${config.dbUser}" + ( echo 'CREATE LANGUAGE plpgsql;' + cat ${mediawikiRoot}/maintenance/postgres/tables.sql + echo 'CREATE TEXT SEARCH CONFIGURATION public.default ( COPY = pg_catalog.english );' + echo COMMIT + ) | ${pkgs.postgresql}/bin/psql -U "${config.dbUser}" "${config.dbName}" + fi + ''); + + robotsEntries = optionalString (config.articleUrlPrefix != "") + '' + User-agent: * + Disallow: ${config.urlPrefix}/ + Disallow: ${config.articleUrlPrefix}/Special:Search + Disallow: ${config.articleUrlPrefix}/Special:Random + ''; + +} diff --git a/nixos/modules/services/web-servers/apache-httpd/mercurial.nix b/nixos/modules/services/web-servers/apache-httpd/mercurial.nix new file mode 100644 index 0000000000000..755b595c783dd --- /dev/null +++ b/nixos/modules/services/web-servers/apache-httpd/mercurial.nix @@ -0,0 +1,75 @@ +{ config, pkgs, serverInfo, ... }: + +let + inherit (pkgs) mercurial; + inherit (pkgs.lib) mkOption; + + urlPrefix = config.urlPrefix; + + cgi = pkgs.stdenv.mkDerivation { + name = "mercurial-cgi"; + buildCommand = '' + ensureDir $out + cp -v ${mercurial}/share/cgi-bin/hgweb.cgi $out + sed -i "s|/path/to/repo/or/config|$out/hgweb.config|" $out/hgweb.cgi + echo " + [collections] + ${config.dataDir} = ${config.dataDir} + [web] + style = gitweb + allow_push = * + " > $out/hgweb.config + ''; + }; + +in { + + extraConfig = '' + RewriteEngine on + RewriteRule /(.*) ${cgi}/hgweb.cgi/$1 + + <Location "${urlPrefix}"> + AuthType Basic + AuthName "Mercurial repositories" + AuthUserFile ${config.dataDir}/hgusers + <LimitExcept GET> + Require valid-user + </LimitExcept> + </Location> + <Directory "${cgi}"> + Order allow,deny + Allow from all + AllowOverride All + Options ExecCGI + AddHandler cgi-script .cgi + PassEnv PYTHONPATH + </Directory> + ''; + + robotsEntries = '' + User-agent: * + Disallow: ${urlPrefix} + ''; + + extraServerPath = [ pkgs.python ]; + + globalEnvVars = [ { name = "PYTHONPATH"; value = "${mercurial}/lib/${pkgs.python.libPrefix}/site-packages"; } ]; + + options = { + urlPrefix = mkOption { + default = "/hg"; + description = " + The URL prefix under which the Mercurial service appears. + Use the empty string to have it appear in the server root. + "; + }; + + dataDir = mkOption { + example = "/data/mercurial"; + description = " + Path to the directory that holds the repositories. + "; + }; + }; + +} diff --git a/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix b/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix new file mode 100644 index 0000000000000..a5227bae2d40d --- /dev/null +++ b/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix @@ -0,0 +1,146 @@ +# 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, mkOption}: + +{ + + hostName = mkOption { + default = "localhost"; + description = " + Canonical hostname for the server. + "; + }; + + serverAliases = mkOption { + default = []; + example = ["www.example.org" "www.example.org:8080" "example.org"]; + description = " + Additional names of virtual hosts served by this virtual host configuration. + "; + }; + + port = mkOption { + default = 0; + description = " + Port for the server. 0 means use the default port: 80 for http + and 443 for https (i.e. when enableSSL is set). + "; + }; + + enableSSL = mkOption { + 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 { + default = ""; + example = "/var/host.cert"; + description = " + Path to server SSL certificate. + "; + }; + + sslServerKey = mkOption { + default = ""; + example = "/var/host.key"; + description = " + Path to server SSL certificate key. + "; + }; + + adminAddr = mkOption ({ + example = "admin@example.org"; + description = " + E-mail address of the server administrator. + "; + } // (if forMainServer then {} else {default = "";})); + + documentRoot = mkOption { + 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 { + default = []; + example = [ + { urlPath = "/nix"; + dir = "/home/eelco/Dev/nix-homepage"; + } + ]; + description = " + This option provides a simple way to serve static directories. + "; + }; + + servedFiles = mkOption { + default = []; + example = [ + { urlPath = "/foo/bar.png"; + dir = "/home/eelco/some-file.png"; + } + ]; + description = " + This option provides a simple way to serve individual, static files. + "; + }; + + extraConfig = mkOption { + 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 { + default = []; + description = " + Extra subservices to enable in the webserver. + "; + }; + + enableUserDir = mkOption { + default = false; + description = " + Whether to enable serving <filename>~/public_html</filename> as + <literal>/~<replaceable>username</replaceable></literal>. + "; + }; + + globalRedirect = mkOption { + default = ""; + example = http://newserver.example.org/; + description = " + If set, all requests for this host are redirected permanently to + the given URL. + "; + }; + + logFormat = mkOption { + default = "common"; + example = "combined"; + description = " + Log format for Apache's log files. Possible values are: combined, common, referer, agent. + "; + }; + +} diff --git a/nixos/modules/services/web-servers/apache-httpd/tomcat-connector.nix b/nixos/modules/services/web-servers/apache-httpd/tomcat-connector.nix new file mode 100644 index 0000000000000..f12ae842b5874 --- /dev/null +++ b/nixos/modules/services/web-servers/apache-httpd/tomcat-connector.nix @@ -0,0 +1,95 @@ +{ config, pkgs, serverInfo, ... }: + +let + extraWorkersProperties = pkgs.lib.optionalString (config ? extraWorkersProperties) config.extraWorkersProperties; + + workersProperties = pkgs.writeText "workers.properties" '' +# Define list of workers that will be used +# for mapping requests +# The configuration directives are valid +# for the mod_jk version 1.2.18 and later +# +worker.list=loadbalancer,status + +# Define Node1 +# modify the host as your host IP or DNS name. +worker.node1.port=8009 +worker.node1.host=localhost +worker.node1.type=ajp13 +worker.node1.lbfactor=1 + +# Load-balancing behaviour +worker.loadbalancer.type=lb +worker.loadbalancer.balance_workers=node1 + +# Status worker for managing load balancer +worker.status.type=status + +${extraWorkersProperties} + ''; +in +{ + extraModules = [ + { name = "jk"; path = "${pkgs.tomcat_connectors}/modules/mod_jk.so"; } + ]; + + extraConfig = '' +# Where to find workers.properties +JkWorkersFile ${workersProperties} + +# Where to put jk logs +JkLogFile ${config.logDir}/mod_jk.log + +# Set the jk log level [debug/error/info] +JkLogLevel info + +# Select the log format +JkLogStampFormat "[%a %b %d %H:%M:%S %Y]" + +# JkOptions indicates to send SSK KEY SIZE +# Note: Changed from +ForwardURICompat. +# See http://tomcat.apache.org/security-jk.html +JkOptions +ForwardKeySize +ForwardURICompatUnparsed -ForwardDirectories + +# JkRequestLogFormat +JkRequestLogFormat "%w %V %T" + +# Mount your applications +JkMount /__application__/* loadbalancer + +# You can use external file for mount points. +# It will be checked for updates each 60 seconds. +# The format of the file is: /url=worker +# /examples/*=loadbalancer +#JkMountFile uriworkermap.properties + +# Add shared memory. +# This directive is present with 1.2.10 and +# later versions of mod_jk, and is needed for +# for load balancing to work properly +# Note: Replaced JkShmFile logs/jk.shm due to SELinux issues. Refer to +# https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=225452 +JkShmFile ${config.stateDir}/jk.shm + +# Static files in all Tomcat webapp context directories are served by apache +JkAutoAlias /var/tomcat/webapps + +# All requests go to worker by default +JkMount /* loadbalancer +# Serve some static files using httpd +#JkUnMount /*.html loadbalancer +#JkUnMount /*.jpg loadbalancer +#JkUnMount /*.gif loadbalancer +#JkUnMount /*.css loadbalancer +#JkUnMount /*.png loadbalancer +#JkUnMount /*.js loadbalancer + +# Add jkstatus for managing runtime data +<Location /jkstatus/> +JkMount status +Order deny,allow +Deny from all +Allow from 127.0.0.1 +</Location> + ''; +} diff --git a/nixos/modules/services/web-servers/apache-httpd/trac.nix b/nixos/modules/services/web-servers/apache-httpd/trac.nix new file mode 100644 index 0000000000000..dc82fd34f2faa --- /dev/null +++ b/nixos/modules/services/web-servers/apache-httpd/trac.nix @@ -0,0 +1,121 @@ +{ config, pkgs, serverInfo, ... }: + +with pkgs.lib; + +let + + # Build a Subversion instance with Apache modules and Swig/Python bindings. + subversion = pkgs.subversion.override (origArgs: { + bdbSupport = true; + httpServer = true; + sslSupport = true; + compressionSupport = true; + pythonBindings = true; + }); + + pythonLib = p: "${p}/"; + +in + +{ + + options = { + + projectsLocation = mkOption { + description = "URL path in which Trac projects can be accessed"; + default = "/projects"; + }; + + projects = mkOption { + description = "List of projects that should be provided by Trac. If they are not defined yet empty projects are created."; + default = []; + example = + [ { identifier = "myproject"; + name = "My Project"; + databaseURL="postgres://root:password@/tracdb"; + subversionRepository="/data/subversion/myproject"; + } + ]; + }; + + user = mkOption { + default = "wwwrun"; + description = "User account under which Trac runs."; + }; + + group = mkOption { + default = "wwwrun"; + description = "Group under which Trac runs."; + }; + + ldapAuthentication = { + enable = mkOption { + default = false; + description = "Enable the ldap authentication in trac"; + }; + + url = mkOption { + default = "ldap://127.0.0.1/dc=example,dc=co,dc=ke?uid?sub?(objectClass=inetOrgPerson)"; + description = "URL of the LDAP authentication"; + }; + + name = mkOption { + default = "Trac server"; + description = "AuthName"; + }; + }; + + }; + + extraModules = singleton + { name = "python"; path = "${pkgs.mod_python}/modules/mod_python.so"; }; + + extraConfig = '' + <Location ${config.projectsLocation}> + SetHandler mod_python + PythonHandler trac.web.modpython_frontend + PythonOption TracEnvParentDir /var/trac/projects + PythonOption TracUriRoot ${config.projectsLocation} + PythonOption PYTHON_EGG_CACHE /var/trac/egg-cache + </Location> + ${if config.ldapAuthentication.enable then '' + <LocationMatch "^${config.projectsLocation}[^/]+/login$"> + AuthType Basic + AuthName "${config.ldapAuthentication.name}" + AuthBasicProvider "ldap" + AuthLDAPURL "${config.ldapAuthentication.url}" + authzldapauthoritative Off + require valid-user + </LocationMatch> + '' else ""} + ''; + + globalEnvVars = singleton + { name = "PYTHONPATH"; + value = + makeSearchPath "lib/${pkgs.python.libPrefix}/site-packages" + [ pkgs.mod_python + pkgs.pythonPackages.trac + pkgs.setuptools + pkgs.pythonPackages.genshi + pkgs.pythonPackages.psycopg2 + pkgs.python.modules.sqlite3 + subversion + ]; + }; + + startupScript = pkgs.writeScript "activateTrac" '' + mkdir -p /var/trac + chown ${config.user}:${config.group} /var/trac + + ${concatMapStrings (project: + '' + if [ ! -d /var/trac/${project.identifier} ] + then + export PYTHONPATH=${pkgs.pythonPackages.psycopg2}/lib/${pkgs.python.libPrefix}/site-packages + ${pkgs.pythonPackages.trac}/bin/trac-admin /var/trac/${project.identifier} initenv "${project.name}" "${project.databaseURL}" svn "${project.subversionRepository}" + fi + '' ) (config.projects)} + ''; + +} diff --git a/nixos/modules/services/web-servers/apache-httpd/zabbix.nix b/nixos/modules/services/web-servers/apache-httpd/zabbix.nix new file mode 100644 index 0000000000000..a6e6042fdf6d9 --- /dev/null +++ b/nixos/modules/services/web-servers/apache-httpd/zabbix.nix @@ -0,0 +1,82 @@ +{ config, pkgs, serverInfo, ... }: + +let + + # The Zabbix PHP frontend needs to be able to write its + # configuration settings (the connection info to the database) to + # the "conf" subdirectory. So symlink $out/conf to some directory + # outside of the Nix store where we want to keep this stateful info. + # Note that different instances of the frontend will therefore end + # up with their own copies of the PHP sources. !!! Alternatively, + # we could generate zabbix.conf.php declaratively. + zabbixPHP = pkgs.runCommand "${pkgs.zabbix.server.name}-php" {} + '' + cp -rs ${pkgs.zabbix.server}/share/zabbix/php "$out" + chmod -R u+w $out + ln -s "${if config.configFile == null + then "${config.stateDir}/zabbix.conf.php" + else config.configFile}" "$out/conf/zabbix.conf.php" + ''; + +in + +{ + + enablePHP = true; + + phpOptions = + '' + post_max_size = 32M + max_execution_time = 300 + max_input_time = 300 + ''; + + extraConfig = '' + Alias ${config.urlPrefix}/ ${zabbixPHP}/ + + <Directory ${zabbixPHP}> + DirectoryIndex index.php + Order deny,allow + Allow from * + </Directory> + ''; + + startupScript = pkgs.writeScript "zabbix-startup-hook" '' + mkdir -p ${config.stateDir} + chown -R ${serverInfo.serverConfig.user} ${config.stateDir} + ''; + + # The frontend needs "ps" to find out whether zabbix_server is running. + extraServerPath = [ pkgs.procps ]; + + options = { + + urlPrefix = pkgs.lib.mkOption { + default = "/zabbix"; + description = " + The URL prefix under which the Zabbix service appears. + Use the empty string to have it appear in the server root. + "; + }; + + configFile = pkgs.lib.mkOption { + default = null; + type = with pkgs.lib.types; nullOr path; + description = '' + The configuration file (zabbix.conf.php) which contains the database + connection settings. If not set, the configuration settings will created + by the web installer. + ''; + }; + + stateDir = pkgs.lib.mkOption { + default = "/var/lib/zabbix/frontend"; + description = " + Directory where the dynamically generated configuration data + of the PHP frontend will be stored. + "; + }; + + }; + +} diff --git a/nixos/modules/services/web-servers/jboss/builder.sh b/nixos/modules/services/web-servers/jboss/builder.sh new file mode 100644 index 0000000000000..2eb89a90f67d5 --- /dev/null +++ b/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/nixos/modules/services/web-servers/jboss/default.nix b/nixos/modules/services/web-servers/jboss/default.nix new file mode 100644 index 0000000000000..e1bcede6563cf --- /dev/null +++ b/nixos/modules/services/web-servers/jboss/default.nix @@ -0,0 +1,83 @@ +{ config, pkgs, ... }: + +with pkgs.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"; + }; + + 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 { + + jobs.jboss = + { description = "JBoss server"; + + exec = "${jbossService}/bin/control start"; + }; + + }; + +} diff --git a/nixos/modules/services/web-servers/lighttpd/cgit.nix b/nixos/modules/services/web-servers/lighttpd/cgit.nix new file mode 100644 index 0000000000000..62264f1db452c --- /dev/null +++ b/nixos/modules/services/web-servers/lighttpd/cgit.nix @@ -0,0 +1,65 @@ +{ config, pkgs, ... }: + +with pkgs.lib; + +let + cfg = config.services.lighttpd.cgit; + configFile = pkgs.writeText "cgitrc" + '' + ${cfg.configText} + ''; +in +{ + + options.services.lighttpd.cgit = { + + enable = mkOption { + default = false; + type = types.uniq types.bool; + description = '' + If true, enable cgit (fast web interface for git repositories) as a + sub-service in lighttpd. cgit will be accessible at + http://yourserver/cgit + ''; + }; + + configText = mkOption { + default = ""; + example = '' + cache-size=1000 + scan-path=/srv/git + ''; + type = types.string; + 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 ]; + + services.lighttpd.extraConfig = '' + $HTTP["url"] =~ "^/cgit" { + cgi.assign = ( + "cgit.cgi" => "${pkgs.cgit}/cgit/cgit.cgi" + ) + alias.url = ( + "/cgit.css" => "${pkgs.cgit}/cgit/cgit.css", + "/cgit.png" => "${pkgs.cgit}/cgit/cgit.png", + "/cgit" => "${pkgs.cgit}/cgit/cgit.cgi" + ) + setenv.add-environment = ( + "CGIT_CONFIG" => "${configFile}" + ) + } + ''; + + }; + +} diff --git a/nixos/modules/services/web-servers/lighttpd/default.nix b/nixos/modules/services/web-servers/lighttpd/default.nix new file mode 100644 index 0000000000000..f9e40fc4b5415 --- /dev/null +++ b/nixos/modules/services/web-servers/lighttpd/default.nix @@ -0,0 +1,178 @@ +# NixOS module for lighttpd web server + +{ config, pkgs, ... }: + +with pkgs.lib; + +let + + cfg = config.services.lighttpd; + + needModRedirect = cfg.gitweb.enable; + needModAlias = cfg.cgit.enable or cfg.gitweb.enable; + needModSetenv = cfg.cgit.enable or cfg.gitweb.enable; + needModCgi = cfg.cgit.enable or cfg.gitweb.enable; + needModStatus = cfg.mod_status; + needModUserdir = cfg.mod_userdir; + + 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 = ( + ${optionalString needModRedirect ''"mod_redirect",''} + ${optionalString needModAlias ''"mod_alias",''} + ${optionalString needModSetenv ''"mod_setenv",''} + ${optionalString needModCgi ''"mod_cgi",''} + ${optionalString needModStatus ''"mod_status",''} + ${optionalString needModUserdir ''"mod_userdir",''} + "mod_accesslog" + ) + + # Logging (logs end up in systemd journal) + accesslog.use-syslog = "enable" + server.errorlog-use-syslog = "enable" + + mimetype.assign = ( + ".html" => "text/html", + ".htm" => "text/html", + ".txt" => "text/plain", + ".jpg" => "image/jpeg", + ".png" => "image/png", + ".css" => "text/css" + ) + + 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.uniq types.bool; + description = '' + Enable the lighttpd web server. + ''; + }; + + port = mkOption { + default = 80; + type = types.uniq types.int; + description = '' + TCP port number for lighttpd to bind to. + ''; + }; + + document-root = mkOption { + default = "/srv/www"; + type = types.uniq types.string; + description = '' + Document-root of the web server. Must be readable by the "lighttpd" user. + ''; + }; + + mod_userdir = mkOption { + default = false; + type = types.uniq 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. + ''; + }; + + mod_status = mkOption { + default = false; + type = types.uniq 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.string; + 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.string; + 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 { + + systemd.services.lighttpd = { + description = "Lighttpd Web Server"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + preStart = '' + ${if cfg.cgit.enable then '' + mkdir -p /var/cache/cgit + chown lighttpd:lighttpd /var/cache/cgit + '' else ""} + ''; + serviceConfig.ExecStart = "${pkgs.lighttpd}/sbin/lighttpd -D -f ${configFile}"; + # SIGINT => graceful shutdown + serviceConfig.KillSignal = "SIGINT"; + }; + + users.extraUsers.lighttpd = { + group = "lighttpd"; + description = "lighttpd web server privilege separation user"; + uid = config.ids.uids.lighttpd; + }; + + users.extraGroups.lighttpd.gid = config.ids.gids.lighttpd; + }; +} diff --git a/nixos/modules/services/web-servers/lighttpd/gitweb.nix b/nixos/modules/services/web-servers/lighttpd/gitweb.nix new file mode 100644 index 0000000000000..d759d8144b64e --- /dev/null +++ b/nixos/modules/services/web-servers/lighttpd/gitweb.nix @@ -0,0 +1,67 @@ +{ config, pkgs, ... }: + +with pkgs.lib; + +let + cfg = config.services.lighttpd.gitweb; + gitwebConfigFile = pkgs.writeText "gitweb.conf" '' + # path to git projects (<project>.git) + $projectroot = "${cfg.projectroot}"; + ${cfg.extraConfig} + ''; + +in +{ + + options.services.lighttpd.gitweb = { + + enable = mkOption { + default = false; + type = types.uniq types.bool; + description = '' + If true, enable gitweb in lighttpd. Access it at http://yourserver/gitweb + ''; + }; + + projectroot = mkOption { + default = "/srv/git"; + type = types.uniq types.string; + description = '' + Path to git projects (bare repositories) that should be served by + gitweb. Must not end with a slash. + ''; + }; + + extraConfig = mkOption { + default = ""; + type = types.uniq types.string; + description = '' + Verbatim configuration text appended to the generated gitweb.conf file. + ''; + }; + + }; + + config = mkIf cfg.enable { + + services.lighttpd.extraConfig = '' + $HTTP["url"] =~ "^/gitweb" { + cgi.assign = ( + ".cgi" => "${pkgs.perl}/bin/perl" + ) + url.redirect = ( + "^/gitweb$" => "/gitweb/" + ) + alias.url = ( + "/gitweb/static/" => "${pkgs.git}/share/gitweb/static/", + "/gitweb/" => "${pkgs.git}/share/gitweb/gitweb.cgi" + ) + setenv.add-environment = ( + "GITWEB_CONFIG" => "${gitwebConfigFile}" + ) + } + ''; + + }; + +} diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix new file mode 100644 index 0000000000000..b26af1aa74455 --- /dev/null +++ b/nixos/modules/services/web-servers/nginx/default.nix @@ -0,0 +1,88 @@ +{ config, pkgs, ... }: + +with pkgs.lib; + +let + cfg = config.services.nginx; + nginx = pkgs.nginx.override { fullWebDAV = cfg.fullWebDAV; }; + configFile = pkgs.writeText "nginx.conf" '' + user ${cfg.user} ${cfg.group}; + daemon off; + ${cfg.config} + ''; +in + +{ + options = { + services.nginx = { + enable = mkOption { + default = false; + description = " + Enable the nginx Web Server. + "; + }; + + config = mkOption { + default = "events {}"; + description = " + Verbatim nginx.conf configuration. + "; + }; + + stateDir = mkOption { + default = "/var/spool/nginx"; + description = " + Directory holding all state for nginx to run. + "; + }; + + user = mkOption { + default = "nginx"; + description = "User account under which nginx runs."; + }; + + group = mkOption { + default = "nginx"; + description = "Group account under which nginx runs."; + }; + + fullWebDAV = mkOption { + default = false; + description = "Compile in a third party module providing full WebDAV support"; + }; + }; + + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ nginx ]; + + # TODO: test user supplied config file pases syntax test + + systemd.services.nginx = { + description = "Nginx Web Server"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + path = [ nginx ]; + preStart = + '' + mkdir -p ${cfg.stateDir}/logs + chown -R ${cfg.user}:${cfg.group} ${cfg.stateDir} + ''; + serviceConfig = { + ExecStart = "${nginx}/bin/nginx -c ${configFile} -p ${cfg.stateDir}"; + }; + }; + + users.extraUsers = optionalAttrs (cfg.user == "nginx") (singleton + { name = "nginx"; + group = "nginx"; + uid = config.ids.uids.nginx; + }); + + users.extraGroups = optionalAttrs (cfg.group == "nginx") (singleton + { name = "nginx"; + gid = config.ids.gids.nginx; + }); + }; +} diff --git a/nixos/modules/services/web-servers/tomcat.nix b/nixos/modules/services/web-servers/tomcat.nix new file mode 100644 index 0000000000000..a68828de5d8e6 --- /dev/null +++ b/nixos/modules/services/web-servers/tomcat.nix @@ -0,0 +1,344 @@ +{ config, pkgs, ... }: + +with pkgs.lib; + +let + + cfg = config.services.tomcat; + tomcat = pkgs.tomcat6; +in + +{ + + ###### interface + + options = { + + services.tomcat = { + + enable = mkOption { + default = false; + description = "Whether to enable Apache Tomcat"; + }; + + baseDir = mkOption { + default = "/var/tomcat"; + description = "Location where Tomcat stores configuration files, webapplications and logfiles"; + }; + + extraGroups = mkOption { + default = []; + example = [ "users" ]; + description = "Defines extra groups to which the tomcat user belongs."; + }; + + user = mkOption { + default = "tomcat"; + description = "User account under which Apache Tomcat runs."; + }; + + group = mkOption { + default = "tomcat"; + description = "Group account under which Apache Tomcat runs."; + }; + + javaOpts = mkOption { + default = ""; + description = "Parameters to pass to the Java Virtual Machine which spawns Apache Tomcat"; + }; + + catalinaOpts = mkOption { + default = ""; + description = "Parameters to pass to the Java Virtual Machine which spawns the Catalina servlet container"; + }; + + sharedLibs = mkOption { + default = []; + description = "List containing JAR files or directories with JAR files which are libraries shared by the web applications"; + }; + + commonLibs = mkOption { + 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 { + default = [ tomcat ]; + description = "List containing WAR files or directories with WAR files which are web applications to be deployed on Tomcat"; + }; + + virtualHosts = mkOption { + default = []; + description = "List consisting of a virtual host name and a list of web applications to deploy on each virtual host"; + }; + + logPerVirtualHost = mkOption { + default = false; + description = "Whether to enable logging per virtual host."; + }; + + axis2 = { + + enable = mkOption { + default = false; + description = "Whether to enable an Apache Axis2 container"; + }; + + services = mkOption { + default = []; + 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.extraGroups = singleton + { name = "tomcat"; + gid = config.ids.gids.tomcat; + }; + + users.extraUsers = singleton + { name = "tomcat"; + uid = config.ids.uids.tomcat; + description = "Tomcat user"; + home = "/homeless-shelter"; + extraGroups = cfg.extraGroups; + }; + + jobs.tomcat = + { description = "Apache Tomcat server"; + + startOn = "started network-interfaces"; + stopOn = "stopping network-interfaces"; + + preStart = + '' + # Create the base directory + mkdir -p ${cfg.baseDir} + + # Create a symlink to the bin directory of the tomcat component + ln -sfn ${tomcat}/bin ${cfg.baseDir}/bin + + # Create a conf/ directory + mkdir -p ${cfg.baseDir}/conf + chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/conf + + # 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 + + # Create subdirectory for virtual hosts + mkdir -p ${cfg.baseDir}/virtualhosts + + # 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 + + # Create a modified server.xml which also includes all virtual hosts + sed -e "/<Engine name=\"Catalina\" defaultHost=\"localhost\">/a\ ${ + toString (map (virtualHost: ''<Host name=\"${virtualHost.name}\" appBase=\"virtualhosts/${virtualHost.name}/webapps\" unpackWARs=\"true\" autoDeploy=\"true\" xmlValidation=\"false\" xmlNamespaceAware=\"false\" >${if cfg.logPerVirtualHost then ''<Valve className=\"org.apache.catalina.valves.AccessLogValve\" directory=\"logs/${virtualHost.name}\" prefix=\"${virtualHost.name}_access_log.\" pattern=\"combined\" resolveHosts=\"false\"/>'' else ""}</Host>'') cfg.virtualHosts)}" \ + ${tomcat}/conf/server.xml > ${cfg.baseDir}/conf/server.xml + + # Create a logs/ directory + mkdir -p ${cfg.baseDir}/logs + chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs + ${if cfg.logPerVirtualHost then + toString (map (h: '' + mkdir -p ${cfg.baseDir}/logs/${h.name} + chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs/${h.name} + '') cfg.virtualHosts) else ''''} + + # Create a temp/ directory + mkdir -p ${cfg.baseDir}/temp + chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/temp + + # Create a lib/ directory + mkdir -p ${cfg.baseDir}/lib + chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/lib + + # Create a shared/lib directory + mkdir -p ${cfg.baseDir}/shared/lib + chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/shared/lib + + # Create a webapps/ directory + mkdir -p ${cfg.baseDir}/webapps + chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/webapps + + # 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) } + + # Create a work/ directory + mkdir -p ${cfg.baseDir}/work + chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/work + + ${if cfg.axis2.enable then + '' + # 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 + '' + else ""} + + ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c 'CATALINA_BASE=${cfg.baseDir} JAVA_HOME=${pkgs.jdk} JAVA_OPTS="${cfg.javaOpts}" CATALINA_OPTS="${cfg.catalinaOpts}" ${tomcat}/bin/startup.sh' + ''; + + postStop = + '' + echo "Stopping tomcat..." + CATALINA_BASE=${cfg.baseDir} JAVA_HOME=${pkgs.jdk} ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c ${tomcat}/bin/shutdown.sh + ''; + + }; + + }; + +} diff --git a/nixos/modules/services/web-servers/varnish/default.nix b/nixos/modules/services/web-servers/varnish/default.nix new file mode 100644 index 0000000000000..7e327120c3d15 --- /dev/null +++ b/nixos/modules/services/web-servers/varnish/default.nix @@ -0,0 +1,63 @@ +{ config, pkgs, ...}: +let + cfg = config.services.varnish; + +in +with pkgs.lib; +{ + options = { + services.varnish = { + enable = mkOption { + default = false; + description = " + Enable the Varnish Server. + "; + }; + + http_address = mkOption { + default = "*:6081"; + description = " + HTTP listen address and port. + "; + }; + + config = mkOption { + description = " + Verbatim default.vcl configuration. + "; + }; + + stateDir = mkOption { + default = "/var/spool/varnish"; + description = " + Directory holding all state for Varnish to run. + "; + }; + }; + + }; + + config = mkIf cfg.enable { + + systemd.services.varnish = { + description = "Varnish"; + wantedBy = [ "multi-user.target" ]; + preStart = '' + mkdir -p ${cfg.stateDir} + chown -R varnish:varnish ${cfg.stateDir} + ''; + path = [ pkgs.gcc ]; + serviceConfig.ExecStart = "${pkgs.varnish}/sbin/varnishd -a ${cfg.http_address} -f ${pkgs.writeText "default.vcl" cfg.config} -n ${cfg.stateDir} -u varnish"; + serviceConfig.Type = "forking"; + }; + + environment.systemPackages = [ pkgs.varnish ]; + + users.extraUsers.varnish = { + group = "varnish"; + uid = config.ids.uids.varnish; + }; + + users.extraGroups.varnish.gid = config.ids.uids.varnish; + }; +} diff --git a/nixos/modules/services/web-servers/zope2.nix b/nixos/modules/services/web-servers/zope2.nix new file mode 100644 index 0000000000000..19afa55d7fef1 --- /dev/null +++ b/nixos/modules/services/web-servers/zope2.nix @@ -0,0 +1,249 @@ +{ pkgs, config, ... }: + +with pkgs.lib; + +let + + cfg = config.services.zope2; + + zope2Opts = { name, config, ... }: { + options = { + + name = mkOption { + default = "${name}"; + type = types.string; + 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.string; + description = "Give a port and adress for the HTTP server."; + }; + + user = mkOption { + default = "zope2"; + type = types.string; + description = "The name of the effective user for the Zope process."; + }; + + 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.string; + 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 = types.loaOf types.optionSet; + example = { + 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."; + options = [ zope2Opts ]; + }; + }; + + ###### implementation + + config = mkIf (cfg.instances != {}) { + + users.extraUsers.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 /var/lib/zope2/${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 /var/lib/zope2/${name}/pid +lock-filename /var/lib/zope2/${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 = "zope2 ${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 /var/lib/zope2/${name}/filestorage /var/lib/zope2/${name}/blobstorage + chown ${opts.user} /var/lib/zope2/${name} -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)); + + }; + +} |