From 72300eb8116c960935a462564d96db6fac355bca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isma=C3=ABl=20Bouya?= Date: Wed, 25 Mar 2020 11:57:48 +0100 Subject: Upgrade nixos --- modules/acme2.nix | 353 ----------------------------- modules/default.nix | 1 - modules/webapps/mastodon.nix | 2 +- modules/webapps/webstats/default.nix | 2 +- modules/websites/default.nix | 28 +-- modules/websites/httpd-service-builder.nix | 68 ++---- modules/websites/php-application.nix | 31 +-- 7 files changed, 53 insertions(+), 432 deletions(-) delete mode 100644 modules/acme2.nix (limited to 'modules') diff --git a/modules/acme2.nix b/modules/acme2.nix deleted file mode 100644 index b22e4ccc..00000000 --- a/modules/acme2.nix +++ /dev/null @@ -1,353 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - - cfg = config.security.acme2; - - certOpts = { name, ... }: { - options = { - webroot = mkOption { - type = types.str; - example = "/var/lib/acme/acme-challenges"; - description = '' - Where the webroot of the HTTP vhost is located. - .well-known/acme-challenge/ directory - will be created below the webroot if it doesn't exist. - http://example.org/.well-known/acme-challenge/ must also - be available (notice unencrypted HTTP). - ''; - }; - - server = mkOption { - type = types.nullOr types.str; - default = null; - description = '' - ACME Directory Resource URI. Defaults to let's encrypt - production endpoint, - https://acme-v02.api.letsencrypt.org/directory, if unset. - ''; - }; - - domain = mkOption { - type = types.str; - default = name; - description = "Domain to fetch certificate for (defaults to the entry name)"; - }; - - email = mkOption { - type = types.nullOr types.str; - default = null; - description = "Contact email address for the CA to be able to reach you."; - }; - - user = mkOption { - type = types.str; - default = "root"; - description = "User running the ACME client."; - }; - - group = mkOption { - type = types.str; - default = "root"; - description = "Group running the ACME client."; - }; - - allowKeysForGroup = mkOption { - type = types.bool; - default = false; - description = '' - Give read permissions to the specified group - () to read SSL private certificates. - ''; - }; - - postRun = mkOption { - type = types.lines; - default = ""; - example = "systemctl reload nginx.service"; - description = '' - Commands to run after new certificates go live. Typically - the web server and other servers using certificates need to - be reloaded. - - Executed in the same directory with the new certificate. - ''; - }; - - plugins = mkOption { - type = types.listOf (types.enum [ - "cert.der" "cert.pem" "chain.pem" "external.sh" - "fullchain.pem" "full.pem" "key.der" "key.pem" "account_key.json" "account_reg.json" - ]); - default = [ "fullchain.pem" "full.pem" "key.pem" "account_key.json" "account_reg.json" ]; - description = '' - Plugins to enable. With default settings simp_le will - store public certificate bundle in fullchain.pem, - private key in key.pem and those two previous - files combined in full.pem in its state directory. - ''; - }; - - directory = mkOption { - type = types.str; - readOnly = true; - default = "/var/lib/acme/${name}"; - description = "Directory where certificate and other state is stored."; - }; - - extraDomains = mkOption { - type = types.attrsOf (types.nullOr types.str); - default = {}; - example = literalExample '' - { - "example.org" = "/srv/http/nginx"; - "mydomain.org" = null; - } - ''; - description = '' - A list of extra domain names, which are included in the one certificate to be issued, with their - own server roots if needed. - ''; - }; - }; - }; - -in - -{ - - ###### interface - imports = [ - (mkRemovedOptionModule [ "security" "acme2" "production" ] '' - Use security.acme2.server to define your staging ACME server URL instead. - - To use the let's encrypt staging server, use security.acme2.server = - "https://acme-staging-v02.api.letsencrypt.org/directory". - '' - ) - (mkRemovedOptionModule [ "security" "acme2" "directory"] "ACME Directory is now hardcoded to /var/lib/acme and its permisisons are managed by systemd. See https://github.com/NixOS/nixpkgs/issues/53852 for more info.") - (mkRemovedOptionModule [ "security" "acme" "preDelay"] "This option has been removed. If you want to make sure that something executes before certificates are provisioned, add a RequiredBy=acme-\${cert}.service to the service you want to execute before the cert renewal") - (mkRemovedOptionModule [ "security" "acme" "activationDelay"] "This option has been removed. If you want to make sure that something executes before certificates are provisioned, add a RequiredBy=acme-\${cert}.service to the service you want to execute before the cert renewal") - ]; - options = { - security.acme2 = { - - validMin = mkOption { - type = types.int; - default = 30 * 24 * 3600; - description = "Minimum remaining validity before renewal in seconds."; - }; - - renewInterval = mkOption { - type = types.str; - default = "weekly"; - description = '' - Systemd calendar expression when to check for renewal. See - systemd.time - 7. - ''; - }; - - server = mkOption { - type = types.nullOr types.str; - default = null; - description = '' - ACME Directory Resource URI. Defaults to let's encrypt - production endpoint, - https://acme-v02.api.letsencrypt.org/directory, if unset. - ''; - }; - - preliminarySelfsigned = mkOption { - type = types.bool; - default = true; - description = '' - Whether a preliminary self-signed certificate should be generated before - doing ACME requests. This can be useful when certificates are required in - a webserver, but ACME needs the webserver to make its requests. - - With preliminary self-signed certificate the webserver can be started and - can later reload the correct ACME certificates. - ''; - }; - - certs = mkOption { - default = { }; - type = with types; attrsOf (submodule certOpts); - description = '' - Attribute set of certificates to get signed and renewed. Creates - acme-''${cert}.{service,timer} systemd units for - each certificate defined here. Other services can add dependencies - to those units if they rely on the certificates being present, - or trigger restarts of the service if certificates get renewed. - ''; - example = literalExample '' - { - "example.com" = { - webroot = "/var/www/challenges/"; - email = "foo@example.com"; - extraDomains = { "www.example.com" = null; "foo.example.com" = "/var/www/foo/"; }; - }; - "bar.example.com" = { - webroot = "/var/www/challenges/"; - email = "bar@example.com"; - }; - } - ''; - }; - }; - }; - - ###### implementation - config = mkMerge [ - (mkIf (cfg.certs != { }) { - - systemd.services = let - services = concatLists servicesLists; - servicesLists = mapAttrsToList certToServices cfg.certs; - certToServices = cert: data: - let - lpath = "acme/${cert}"; - rights = if data.allowKeysForGroup then "750" else "700"; - cmdline = [ "-v" "-d" data.domain "--default_root" data.webroot "--valid_min" cfg.validMin ] - ++ optionals (data.email != null) [ "--email" data.email ] - ++ concatMap (p: [ "-f" p ]) data.plugins - ++ concatLists (mapAttrsToList (name: root: [ "-d" (if root == null then name else "${name}:${root}")]) data.extraDomains) - ++ optionals (cfg.server != null || data.server != null) ["--server" (if data.server == null then cfg.server else data.server)]; - acmeService = { - description = "Renew ACME Certificate for ${cert}"; - after = [ "network.target" "network-online.target" ]; - wants = [ "network-online.target" ]; - # simp_le uses requests, which uses certifi under the hood, - # which doesn't respect the system trust store. - # At least in the acme test, we provision a fake CA, impersonating the LE endpoint. - # REQUESTS_CA_BUNDLE is a way to teach python requests to use something else - environment.REQUESTS_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt"; - serviceConfig = { - Type = "oneshot"; - # With RemainAfterExit the service is considered active even - # after the main process having exited, which means when it - # gets changed, the activation phase restarts it, meaning - # the permissions of the StateDirectory get adjusted - # according to the specified group - # Edit: Timers will never run because of this - # RemainAfterExit = true; - SuccessExitStatus = [ "0" "1" ]; - User = data.user; - Group = data.group; - PrivateTmp = true; - StateDirectory = lpath; - StateDirectoryMode = rights; - ExecStartPre = - let - script = pkgs.writeScript "acme-pre-start" '' - #!${pkgs.runtimeShell} -e - mkdir -p '${data.webroot}/.well-known/acme-challenge' - chmod a+w '${data.webroot}/.well-known/acme-challenge' - #doesn't work for multiple concurrent runs - #chown -R '${data.user}:${data.group}' '${data.webroot}/.well-known/acme-challenge' - ''; - in - "+${script}"; - WorkingDirectory = "/var/lib/${lpath}"; - ExecStart = "${pkgs.simp_le_0_17}/bin/simp_le ${escapeShellArgs cmdline}"; - ExecStartPost = - let - script = pkgs.writeScript "acme-post-start" '' - #!${pkgs.runtimeShell} -e - ${data.postRun} - ''; - in - "+${script}"; - }; - - }; - selfsignedService = { - description = "Create preliminary self-signed certificate for ${cert}"; - path = [ pkgs.openssl ]; - script = - '' - workdir="$(mktemp -d)" - - # Create CA - openssl genrsa -des3 -passout pass:xxxx -out $workdir/ca.pass.key 2048 - openssl rsa -passin pass:xxxx -in $workdir/ca.pass.key -out $workdir/ca.key - openssl req -new -key $workdir/ca.key -out $workdir/ca.csr \ - -subj "/C=UK/ST=Warwickshire/L=Leamington/O=OrgName/OU=Security Department/CN=example.com" - openssl x509 -req -days 1 -in $workdir/ca.csr -signkey $workdir/ca.key -out $workdir/ca.crt - - # Create key - openssl genrsa -des3 -passout pass:xxxx -out $workdir/server.pass.key 2048 - openssl rsa -passin pass:xxxx -in $workdir/server.pass.key -out $workdir/server.key - openssl req -new -key $workdir/server.key -out $workdir/server.csr \ - -subj "/C=UK/ST=Warwickshire/L=Leamington/O=OrgName/OU=IT Department/CN=example.com" - openssl x509 -req -days 1 -in $workdir/server.csr -CA $workdir/ca.crt \ - -CAkey $workdir/ca.key -CAserial $workdir/ca.srl -CAcreateserial \ - -out $workdir/server.crt - - # Copy key to destination - cp $workdir/server.key /var/lib/${lpath}/key.pem - - # Create fullchain.pem (same format as "simp_le ... -f fullchain.pem" creates) - cat $workdir/{server.crt,ca.crt} > "/var/lib/${lpath}/fullchain.pem" - - # Create full.pem for e.g. lighttpd - cat $workdir/{server.key,server.crt,ca.crt} > "/var/lib/${lpath}/full.pem" - - # Give key acme permissions - chown '${data.user}:${data.group}' "/var/lib/${lpath}/"{key,fullchain,full}.pem - chmod ${rights} "/var/lib/${lpath}/"{key,fullchain,full}.pem - ''; - serviceConfig = { - Type = "oneshot"; - PrivateTmp = true; - StateDirectory = lpath; - User = data.user; - Group = data.group; - }; - unitConfig = { - # Do not create self-signed key when key already exists - ConditionPathExists = "!/var/lib/${lpath}/key.pem"; - }; - }; - in ( - [ { name = "acme-${cert}"; value = acmeService; } ] - ++ optional cfg.preliminarySelfsigned { name = "acme-selfsigned-${cert}"; value = selfsignedService; } - ); - servicesAttr = listToAttrs services; - in - servicesAttr; - - # FIXME: this doesn't work for multiple users - systemd.tmpfiles.rules = - flip mapAttrsToList cfg.certs - (cert: data: "d ${data.webroot}/.well-known/acme-challenge - ${data.user} ${data.group}"); - - systemd.timers = flip mapAttrs' cfg.certs (cert: data: nameValuePair - ("acme-${cert}") - ({ - description = "Renew ACME Certificate for ${cert}"; - wantedBy = [ "timers.target" ]; - timerConfig = { - OnCalendar = cfg.renewInterval; - Unit = "acme-${cert}.service"; - Persistent = "yes"; - AccuracySec = "5m"; - RandomizedDelaySec = "1h"; - }; - }) - ); - - systemd.targets.acme-selfsigned-certificates = mkIf cfg.preliminarySelfsigned {}; - systemd.targets.acme-certificates = {}; - }) - - ]; - - meta = { - maintainers = with lib.maintainers; [ abbradar fpletz globin ]; - #doc = ./acme.xml; - }; -} diff --git a/modules/default.nix b/modules/default.nix index 98dc77d8..9ff6ea62 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -19,5 +19,4 @@ php-application = ./websites/php-application.nix; websites = ./websites; - acme2 = ./acme2.nix; } // (if builtins.pathExists ./private then import ./private else {}) diff --git a/modules/webapps/mastodon.nix b/modules/webapps/mastodon.nix index eed9e3f6..68531cf3 100644 --- a/modules/webapps/mastodon.nix +++ b/modules/webapps/mastodon.nix @@ -27,7 +27,7 @@ in ''; }; socketsPrefix = lib.mkOption { - type = lib.types.string; + type = lib.types.str; default = "live"; description = '' The prefix to use for Mastodon sockets. diff --git a/modules/webapps/webstats/default.nix b/modules/webapps/webstats/default.nix index e822645c..fe5f068d 100644 --- a/modules/webapps/webstats/default.nix +++ b/modules/webapps/webstats/default.nix @@ -23,7 +23,7 @@ in { ''; }; name = lib.mkOption { - type = lib.types.string; + type = lib.types.str; description = '' Domain name. Corresponds to the Apache file name and the folder name in which the state will be saved. diff --git a/modules/websites/default.nix b/modules/websites/default.nix index 767a7b23..3f46e65d 100644 --- a/modules/websites/default.nix +++ b/modules/websites/default.nix @@ -38,7 +38,7 @@ in description = "Name of the httpd instance to assign this type to"; }; ips = mkOption { - type = listOf string; + type = listOf str; default = []; description = "ips to listen to"; }; @@ -59,7 +59,7 @@ in options = { enable = mkEnableOption "Add default no-ssl vhost for this instance"; host = mkOption { - type = string; + type = str; description = "The hostname to use for this vhost"; }; root = mkOption { @@ -68,7 +68,7 @@ in description = "The root folder to serve"; }; indexFile = mkOption { - type = string; + type = str; default = "index.html"; description = "The index file to show."; }; @@ -79,8 +79,8 @@ in description = "The fallback vhost that will be defined as first vhost in Apache"; type = submodule { options = { - certName = mkOption { type = string; }; - hosts = mkOption { type = listOf string; }; + certName = mkOption { type = str; }; + hosts = mkOption { type = listOf str; }; root = mkOption { type = nullOr path; }; extraConfig = mkOption { type = listOf lines; default = []; }; }; @@ -91,7 +91,7 @@ in description = "List of no ssl vhosts to define for Apache"; type = attrsOf (submodule { options = { - hosts = mkOption { type = listOf string; }; + hosts = mkOption { type = listOf str; }; root = mkOption { type = nullOr path; }; extraConfig = mkOption { type = listOf lines; default = []; }; }; @@ -102,25 +102,25 @@ in description = "List of vhosts to define for Apache"; type = attrsOf (submodule { options = { - certName = mkOption { type = string; }; + certName = mkOption { type = str; }; addToCerts = mkOption { type = bool; default = false; description = "Use these to certificates. Is ignored (considered true) if certMainHost is not null"; }; certMainHost = mkOption { - type = nullOr string; + type = nullOr str; description = "Use that host as 'main host' for acme certs"; default = null; }; - hosts = mkOption { type = listOf string; }; + hosts = mkOption { type = listOf str; }; root = mkOption { type = nullOr path; }; extraConfig = mkOption { type = listOf lines; default = []; }; }; }); }; watchPaths = mkOption { - type = listOf string; + type = listOf str; default = []; description = '' Paths to watch that should trigger a reload of httpd @@ -178,9 +178,9 @@ in }; toVhost = ips: vhostConf: { enableSSL = true; - sslServerCert = "${config.security.acme2.certs."${vhostConf.certName}".directory}/cert.pem"; - sslServerKey = "${config.security.acme2.certs."${vhostConf.certName}".directory}/key.pem"; - sslServerChain = "${config.security.acme2.certs."${vhostConf.certName}".directory}/chain.pem"; + sslServerCert = "${config.security.acme.certs."${vhostConf.certName}".directory}/cert.pem"; + sslServerKey = "${config.security.acme.certs."${vhostConf.certName}".directory}/key.pem"; + sslServerChain = "${config.security.acme.certs."${vhostConf.certName}".directory}/chain.pem"; logFormat = "combinedVhost"; listen = map (ip: { inherit ip; port = 443; }) ips; hostName = builtins.head vhostConf.hosts; @@ -231,7 +231,7 @@ in } ) cfg.env; - config.security.acme2.certs = let + config.security.acme.certs = let typesToManage = attrsets.filterAttrs (k: v: v.enable) cfg.env; flatVhosts = lists.flatten (attrsets.mapAttrsToList (k: v: attrValues v.vhostConfs diff --git a/modules/websites/httpd-service-builder.nix b/modules/websites/httpd-service-builder.nix index d049202c..f0208ab5 100644 --- a/modules/websites/httpd-service-builder.nix +++ b/modules/websites/httpd-service-builder.nix @@ -11,8 +11,6 @@ let httpd = mainCfg.package.out; - version24 = !versionOlder httpd.version "2.4"; - httpdConf = mainCfg.configFile; php = mainCfg.phpPackage.override { apacheHttpd = httpd.dev; /* otherwise it only gets .out */ }; @@ -26,10 +24,9 @@ let else [{ip = "*"; port = 80;}]; getListen = cfg: - let list = (lib.optional (cfg.port != 0) {ip = "*"; port = cfg.port;}) ++ cfg.listen; - in if list == [] - then defaultListen cfg - else list; + if cfg.listen == [] + then defaultListen cfg + else cfg.listen; listenToString = l: "${l.ip}:${toString l.port}"; @@ -110,11 +107,10 @@ let "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") + "authn_file" "authn_dbm" "authn_anon" "authn_core" # Authorization: is the user allowed access? - "authz_user" "authz_groupfile" "authz_host" + "authz_user" "authz_groupfile" "authz_host" "authz_core" # Other modules. "ext_filter" "include" "log_config" "env" "mime_magic" @@ -122,14 +118,9 @@ let "mime" "dav" "status" "autoindex" "asis" "info" "dav_fs" "vhost_alias" "negotiation" "dir" "imagemap" "actions" "speling" "userdir" "alias" "rewrite" "proxy" "proxy_http" - ] - ++ optionals version24 [ + "unixd" "cache" "cache_disk" "slotmem_shm" "socache_shmcb" "mpm_${mainCfg.multiProcessingModule}" - "authz_core" - "unixd" - "cache" "cache_disk" - "slotmem_shm" - "socache_shmcb" + # For compatibility with old configurations, the new module mod_access_compat is provided. "access_compat" ] @@ -138,19 +129,8 @@ let ++ 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 - ''; + allDenied = "Require all denied"; + allGranted = "Require all granted"; loggingConf = (if mainCfg.logFormat != "none" then '' @@ -183,9 +163,9 @@ let sslConf = '' - SSLSessionCache ${if version24 then "shmcb" else "shm"}:${mainCfg.stateDir}/ssl_scache(512000) + SSLSessionCache shmcb:${mainCfg.stateDir}/ssl_scache(512000) - ${if version24 then "Mutex" else "SSLMutex"} posixsem + Mutex posixsem SSLRandomSeed startup builtin SSLRandomSeed connect builtin @@ -325,9 +305,7 @@ let ServerRoot ${httpd} - ${optionalString version24 '' - DefaultRuntimeDir ${mainCfg.stateDir}/runtime - ''} + DefaultRuntimeDir ${mainCfg.stateDir}/runtime PidFile ${mainCfg.stateDir}/httpd.pid @@ -361,7 +339,7 @@ let ++ optional enablePerl { name = "perl"; path = "${mod_perl}/modules/mod_perl.so"; } ++ concatMap (svc: svc.extraModules) allSubservices ++ extraForeignModules; - in concatMapStrings load allModules + in concatMapStrings load (unique allModules) } AddHandler type-map var @@ -393,14 +371,6 @@ let # Generate directives for the main server. ${perServerConf true mainCfg} - # Always enable virtual hosts; it doesn't seem to hurt. - ${let - listen = concatMap getListen allHosts; - uniqueListen = uniqList {inputList = listen;}; - directives = concatMapStrings (listen: "NameVirtualHost ${listenToString listen}\n") uniqueListen; - in optionalString (!version24) directives - } - ${let makeVirtualHost = vhost: '' @@ -663,7 +633,7 @@ in message = "SSL is enabled for httpd, but sslServerCert and/or sslServerKey haven't been specified."; } ]; - warnings = map (cfg: ''apache-httpd's port option is deprecated. Use listen = [{/*ip = "*"; */ port = ${toString cfg.port};}]; instead'' ) (lib.filter (cfg: cfg.port != 0) allHosts); + 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 (withUsers && mainCfg.user == "wwwrun") (singleton { name = "wwwrun"; @@ -686,7 +656,7 @@ in ; Don't advertise PHP expose_php = off - '' + optionalString (!isNull config.time.timeZone) '' + '' + optionalString (config.time.timeZone != null) '' ; Apparently PHP doesn't use $TZ. date.timezone = "${config.time.timeZone}" @@ -713,10 +683,10 @@ in '' mkdir -m 0750 -p ${mainCfg.stateDir} [ $(id -u) != 0 ] || chown root.${mainCfg.group} ${mainCfg.stateDir} - ${optionalString version24 '' - mkdir -m 0750 -p "${mainCfg.stateDir}/runtime" - [ $(id -u) != 0 ] || chown root.${mainCfg.group} "${mainCfg.stateDir}/runtime" - ''} + + 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 diff --git a/modules/websites/php-application.nix b/modules/websites/php-application.nix index 8ad7a0df..20e2a5dd 100644 --- a/modules/websites/php-application.nix +++ b/modules/websites/php-application.nix @@ -44,10 +44,15 @@ in description = "Name of the socket to listen to. Defaults to app name if null"; }; phpPool = mkOption { - type = lines; - default = ""; + type = attrsOf str; + default = {}; description = "Pool configuration to append"; }; + phpEnv = mkOption { + type = attrsOf str; + default = {}; + description = "Pool environment to append"; + }; phpOptions = mkOption { type = lines; default = ""; @@ -135,7 +140,7 @@ in services.phpApplication.phpListenPaths = mkOption { type = attrsOf path; default = attrsets.mapAttrs' (name: icfg: attrsets.nameValuePair - name "/run/phpfpm/${if icfg.phpListen == null then name else icfg.phpListen}.sock" + name config.services.phpfpm.pools."${name}".socket ) cfg.apps; readOnly = true; description = '' @@ -162,17 +167,17 @@ in services.phpfpm.pools = attrsets.mapAttrs' (name: icfg: attrsets.nameValuePair name { - listen = cfg.phpListenPaths."${name}"; - extraConfig = '' - user = ${icfg.httpdUser} - group = ${icfg.httpdGroup} - listen.owner = ${icfg.httpdUser} - listen.group = ${icfg.httpdGroup} - ${optionalString (icfg.phpSession) '' - php_admin_value[session.save_path] = "${icfg.varDir}/phpSessions"''} - php_admin_value[open_basedir] = "${builtins.concatStringsSep ":" ([icfg.app icfg.varDir] ++ icfg.phpWatchFiles ++ icfg.phpOpenbasedir)}" - '' + icfg.phpPool; + user = icfg.httpdUser; + group = icfg.httpdUser; + settings = { + "listen.owner" = icfg.httpdUser; + "listen.group" = icfg.httpdGroup; + "php_admin_value[open_basedir]" = builtins.concatStringsSep ":" ([icfg.app icfg.varDir] ++ icfg.phpWatchFiles ++ icfg.phpOpenbasedir); + } + // optionalAttrs (icfg.phpSession) { "php_admin_value[session.save_path]" = "${icfg.varDir}/phpSessions"; } + // icfg.phpPool; phpOptions = config.services.phpfpm.phpOptions + icfg.phpOptions; + inherit (icfg) phpEnv; } ) cfg.apps; -- cgit v1.2.3