]>
Commit | Line | Data |
---|---|---|
1 | { lib, pkgs, config, ... }: | |
2 | let | |
3 | scfg = config.secrets.fullPaths; | |
4 | cfg = config.myServices.tools.cloud.farm; | |
5 | apacheUser = config.services.websites.env.production.user; | |
6 | apacheGroup = config.services.websites.env.production.group; | |
7 | additionalConfs = icfg: lib.attrsets.mapAttrs (n: v: pkgs.writeText "${n}.json" (builtins.toJSON v)) icfg.rootDir.otherConfig; | |
8 | overrideConfig = icfg: pkgs.writeText "override.config.php" '' | |
9 | <?php | |
10 | $CONFIG = json_decode(file_get_contents("${icfg.configOverride}"), TRUE); | |
11 | ''; | |
12 | toVhost = icfg: '' | |
13 | SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 | |
14 | SetEnv NEXTCLOUD_CONFIG_DIR "${icfg.varDir}/config" | |
15 | <Directory ${icfg.rootDir}> | |
16 | AcceptPathInfo On | |
17 | DirectoryIndex index.php | |
18 | Options FollowSymlinks | |
19 | Require all granted | |
20 | AllowOverride all | |
21 | ||
22 | <IfModule mod_headers.c> | |
23 | Header always set Strict-Transport-Security "max-age=15552000; includeSubDomains; preload" | |
24 | </IfModule> | |
25 | <FilesMatch "\.php$"> | |
26 | CGIPassAuth on | |
27 | SetHandler "proxy:unix:${config.services.phpfpm.pools.${icfg.phpPoolName}.socket}|fcgi://localhost" | |
28 | </FilesMatch> | |
29 | ||
30 | </Directory> | |
31 | ''; | |
32 | in | |
33 | { | |
34 | options.myServices.tools.cloud.farm = { | |
35 | instances = lib.mkOption { | |
36 | description = "Instances names for the nextcloud Farm"; | |
37 | default = {}; | |
38 | type = lib.types.attrsOf (lib.types.submodule ({ name, config, ... }: { | |
39 | options = { | |
40 | nextcloud = lib.mkOption { | |
41 | description = "Nextcloud version to use"; | |
42 | default = pkgs.webapps-nextcloud_27; | |
43 | type = lib.types.package; | |
44 | }; | |
45 | apps = lib.mkOption { | |
46 | description = "Applications to use"; | |
47 | default = a: []; | |
48 | #type = functionTo (listOf packages) | |
49 | type = lib.types.unspecified; | |
50 | }; | |
51 | config = lib.mkOption { | |
52 | description = "Config keys"; | |
53 | default = {}; | |
54 | type = lib.types.attrsOf lib.types.unspecified; | |
55 | }; | |
56 | secretsPath = lib.mkOption { | |
57 | description = "Path in secrets to nextcloud config file"; | |
58 | default = "websites/${name}/nextcloud"; | |
59 | type = lib.types.str; | |
60 | }; | |
61 | configOverride = lib.mkOption { | |
62 | description = "Path to config override"; | |
63 | readOnly = true; | |
64 | default = scfg."${config.secretsPath}"; | |
65 | type = lib.types.path; | |
66 | }; | |
67 | phpPackage = lib.mkOption { | |
68 | description = "PHP package to use"; | |
69 | default = pkgs.php81; | |
70 | type = lib.types.package; | |
71 | apply = v: (v.withExtensions({ enabled, all }: enabled ++ [ all.redis all.apcu all.opcache all.imagick all.sysvsem ])).override { extraConfig = '' | |
72 | apc.enable_cli = 1 | |
73 | apc.enabled = 1 | |
74 | ''; | |
75 | }; | |
76 | }; | |
77 | rootDir = lib.mkOption { | |
78 | description = "Instance root dirs"; | |
79 | readOnly = true; | |
80 | type = lib.types.package; | |
81 | default = config.nextcloud.withApps config.apps; | |
82 | }; | |
83 | phpPoolName = lib.mkOption { | |
84 | description = "Php pool name for the instance"; | |
85 | readOnly = true; | |
86 | type = lib.types.str; | |
87 | default = "nextcloud_farm_" + name; | |
88 | }; | |
89 | phpBaseDir = lib.mkOption { | |
90 | description = "Php basedir for the instance"; | |
91 | readOnly = true; | |
92 | type = lib.types.str; | |
93 | default = builtins.concatStringsSep ":" ( | |
94 | [ config.rootDir config.varDir ] | |
95 | ++ config.rootDir.apps | |
96 | ++ [ config.configOverride (overrideConfig config) ] | |
97 | ++ (builtins.attrValues (additionalConfs config)) | |
98 | ); | |
99 | }; | |
100 | varDir = lib.mkOption { | |
101 | description = "Instance var dir"; | |
102 | type = lib.types.path; | |
103 | default = "/var/lib/nextcloud_farm/${name}"; | |
104 | }; | |
105 | vhost = lib.mkOption { | |
106 | description = "Instance vhost config"; | |
107 | readOnly = true; | |
108 | type = lib.types.str; | |
109 | default = toVhost config; | |
110 | }; | |
111 | }; | |
112 | })); | |
113 | }; | |
114 | }; | |
115 | ||
116 | config = lib.mkIf (builtins.length (builtins.attrNames cfg.instances) > 0) { | |
117 | systemd.services = lib.mapAttrs' (k: v: lib.nameValuePair ("phpfpm-" + v.phpPoolName) { | |
118 | after = lib.mkAfter [ "postgresql.service" ]; | |
119 | wants = [ "postgresql.service" ]; | |
120 | serviceConfig.ExecStartPre = | |
121 | "+${pkgs.writeScript "phpfpm-nextcloud-${k}-pre-start" '' | |
122 | #!${pkgs.stdenv.shell} | |
123 | ||
124 | install -m 0755 -o wwwrun -g wwwrun -d ${v.varDir} -d ${v.varDir}/config | |
125 | ${builtins.concatStringsSep "\n" (lib.attrsets.mapAttrsToList (n: f: | |
126 | "ln -sf ${f} ${v.varDir}/config/${n}.json" | |
127 | ) (additionalConfs v))} | |
128 | ln -sf ${overrideConfig v} ${v.varDir}/config/override.config.php | |
129 | ''}"; | |
130 | }) cfg.instances; | |
131 | services.phpfpm.pools = lib.mapAttrs' (k: v: lib.nameValuePair v.phpPoolName { | |
132 | user = apacheUser; | |
133 | group = apacheGroup; | |
134 | settings = { | |
135 | "listen.owner" = apacheUser; | |
136 | "listen.group" = apacheGroup; | |
137 | "pm" = "dynamic"; | |
138 | "pm.max_children" = "60"; | |
139 | "pm.start_servers" = "3"; | |
140 | "pm.min_spare_servers" = "3"; | |
141 | "pm.max_spare_servers" = "3"; | |
142 | "pm.process_idle_timeout" = "60"; | |
143 | ||
144 | "php_admin_value[output_buffering]" = "0"; | |
145 | "php_admin_value[max_execution_time]" = "1800"; | |
146 | "php_admin_value[zend_extension]" = "opcache"; | |
147 | "php_value[apc.enable_cli]" = "1"; | |
148 | "php_value[apc.enabled]" = "1"; | |
149 | #already enabled by default? | |
150 | #"php_value[opcache.enable]" = "1"; | |
151 | "php_value[opcache.enable_cli]" = "1"; | |
152 | "php_value[opcache.interned_strings_buffer]" = "32"; | |
153 | "php_value[opcache.max_accelerated_files]" = "10000"; | |
154 | "php_value[opcache.memory_consumption]" = "128"; | |
155 | "php_value[opcache.save_comments]" = "1"; | |
156 | "php_value[opcache.revalidate_freq]" = "1"; | |
157 | "php_admin_value[memory_limit]" = "512M"; | |
158 | ||
159 | "php_admin_value[open_basedir]" = "/run/wrappers/bin/sendmail:${v.phpBaseDir}:/proc/cpuinfo:/proc/meminfo:/dev/urandom:/proc/self/fd:/tmp"; | |
160 | "php_admin_value[session.save_handler]" = "redis"; | |
161 | "php_admin_value[session.save_path]" = "'unix:///run/redis-php-sessions/redis.sock?persistent=1&prefix=Tools:NextcloudFarm:${k}:'"; | |
162 | }; | |
163 | phpPackage = v.phpPackage; | |
164 | }) cfg.instances; | |
165 | environment.systemPackages = let | |
166 | toOcc = name: icfg: pkgs.writeScriptBin "nextcloud-occ-${name}" '' | |
167 | #! ${pkgs.stdenv.shell} | |
168 | cd ${icfg.rootDir} | |
169 | NEXTCLOUD_CONFIG_DIR="${icfg.varDir}/config" \ | |
170 | exec \ | |
171 | sudo -E -u wwwrun ${icfg.phpPackage}/bin/php \ | |
172 | -d memory_limit=512M \ | |
173 | -c ${icfg.phpPackage}/etc/php.ini \ | |
174 | occ $* | |
175 | ''; | |
176 | in lib.mapAttrsToList toOcc cfg.instances; | |
177 | services.cron = { | |
178 | enable = true; | |
179 | systemCronJobs = let | |
180 | toScript = name: icfg: pkgs.writeScriptBin "nextcloud-cron" '' | |
181 | #! ${pkgs.stdenv.shell} | |
182 | export LOCALE_ARCHIVE=/run/current-system/sw/lib/locale/locale-archive | |
183 | export PATH=/run/wrappers/bin:$PATH | |
184 | export NEXTCLOUD_CONFIG_DIR="${icfg.varDir}/config" | |
185 | ${icfg.phpPackage}/bin/php -c ${icfg.phpPackage}/etc/php.ini -d memory_limit=512M -f ${icfg.rootDir}/cron.php | |
186 | ''; | |
187 | toLine = name: icfg: '' | |
188 | */5 * * * * wwwrun ${toScript name icfg}/bin/nextcloud-cron | |
189 | ''; | |
190 | in lib.mapAttrsToList toLine cfg.instances; | |
191 | }; | |
192 | ||
193 | secrets.keys = lib.mapAttrs' (name: v: lib.nameValuePair "${v.secretsPath}" { | |
194 | user = "wwwrun"; | |
195 | group = "wwwrun"; | |
196 | permissions = "0600"; | |
197 | # Be careful when editing that: config from this file takes | |
198 | # precedence over the regular one, but if a key got removed, it my | |
199 | # still exist in the default config file | |
200 | text = builtins.toJSON ( { | |
201 | "datadirectory" = if name == "immae" then v.varDir else "${v.varDir}/data"; | |
202 | ||
203 | "appstoreenabled" = false; | |
204 | "integrity.check.disabled" = true; | |
205 | "updater.release.channel" = "stable"; | |
206 | "upgrade.disable-web" = true; | |
207 | ||
208 | "memcache.local" = "\\OC\\Memcache\\APCu"; | |
209 | ||
210 | "htaccess.RewriteBase" = "/"; | |
211 | ||
212 | "loglevel" = 2; | |
213 | "logtimezone" = "Europe/Paris"; | |
214 | ||
215 | "default_phone_region" = "FR"; | |
216 | "skeletondirectory" = ""; | |
217 | "theme" = ""; | |
218 | } // v.config); | |
219 | }) cfg.instances; | |
220 | }; | |
221 | } |