diff options
Diffstat (limited to 'systems/eldiron/websites/cloud')
-rw-r--r-- | systems/eldiron/websites/cloud/add-htaccess.php | 70 | ||||
-rw-r--r-- | systems/eldiron/websites/cloud/default.nix | 151 | ||||
-rw-r--r-- | systems/eldiron/websites/cloud/farm.nix | 221 |
3 files changed, 442 insertions, 0 deletions
diff --git a/systems/eldiron/websites/cloud/add-htaccess.php b/systems/eldiron/websites/cloud/add-htaccess.php new file mode 100644 index 0000000..e11d943 --- /dev/null +++ b/systems/eldiron/websites/cloud/add-htaccess.php | |||
@@ -0,0 +1,70 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace bantu\IniGetWrapper { class IniGetWrapper { } } | ||
4 | namespace Psr\Log { class LoggerInterface { } } | ||
5 | namespace OCP { | ||
6 | class IL10N { } | ||
7 | class Defaults { } | ||
8 | } | ||
9 | namespace OCP\Security { | ||
10 | class ISecureRandom { } | ||
11 | } | ||
12 | namespace OC { | ||
13 | class Installer { } | ||
14 | class SystemConfig { | ||
15 | private $a; | ||
16 | public function __construct($a) { | ||
17 | $this->a = $a; | ||
18 | } | ||
19 | public function getValue($val, $default) { | ||
20 | if(isset($this->a[$val])) { | ||
21 | return $this->a[$val]; | ||
22 | } else { | ||
23 | return $default; | ||
24 | } | ||
25 | } | ||
26 | } | ||
27 | } | ||
28 | |||
29 | namespace { | ||
30 | class NServer { | ||
31 | private $argv; | ||
32 | public function __construct($argv) { | ||
33 | $this->argv = $argv; | ||
34 | } | ||
35 | public function getSystemConfig() { | ||
36 | return new OC\SystemConfig(Array( | ||
37 | 'htaccess.RewriteBase' => $this->argv[1], | ||
38 | )); | ||
39 | } | ||
40 | |||
41 | public function get($c) { | ||
42 | return new $c(); | ||
43 | } | ||
44 | public function getL10N() { | ||
45 | return new \OCP\IL10N(); | ||
46 | } | ||
47 | public function query($c) { | ||
48 | return new $c(); | ||
49 | } | ||
50 | public function getSecureRandom() { | ||
51 | return new \OCP\Security\ISecureRandom(); | ||
52 | } | ||
53 | } | ||
54 | |||
55 | class OC { | ||
56 | public static Bool $CLI = false; | ||
57 | public static $SERVERROOT = '.'; | ||
58 | public static $WEBROOT; | ||
59 | public static \NServer $server; | ||
60 | } | ||
61 | |||
62 | \OC::$server = new NServer($argv); | ||
63 | \OC::$WEBROOT = $argv[1]; | ||
64 | |||
65 | require "./lib/private/Setup.php"; | ||
66 | $result = \OC\Setup::updateHtaccess(); | ||
67 | if ($result) { | ||
68 | echo "done"; | ||
69 | }; | ||
70 | } | ||
diff --git a/systems/eldiron/websites/cloud/default.nix b/systems/eldiron/websites/cloud/default.nix new file mode 100644 index 0000000..e1df883 --- /dev/null +++ b/systems/eldiron/websites/cloud/default.nix | |||
@@ -0,0 +1,151 @@ | |||
1 | { lib, pkgs, config, ... }: | ||
2 | let | ||
3 | ncfg = config.myServices.tools.cloud.farm.instances.immae; | ||
4 | env = config.myEnv.tools.nextcloud; | ||
5 | cfg = config.myServices.websites.tools.cloud; | ||
6 | in { | ||
7 | options.myServices.websites.tools.cloud = { | ||
8 | enable = lib.mkEnableOption "enable cloud website"; | ||
9 | }; | ||
10 | |||
11 | config = lib.mkIf cfg.enable { | ||
12 | myServices.dns.zones."immae.eu".subdomains.cloud = | ||
13 | with config.myServices.dns.helpers; ips servers.eldiron.ips.main; | ||
14 | |||
15 | myServices.chatonsProperties.hostings.nextcloud = { | ||
16 | file.datetime = "2022-08-21T19:50:00"; | ||
17 | hosting = { | ||
18 | name = "Nextcloud"; | ||
19 | description = "The self-hosted productivity platform that keeps you in control"; | ||
20 | website = "https://cloud.immae.eu/"; | ||
21 | logo = "https://cloud.immae.eu/core/img/favicon.ico"; | ||
22 | type = "INSTANCE"; | ||
23 | status.level = "OK"; | ||
24 | status.description = "OK"; | ||
25 | registration.load = "OPEN"; | ||
26 | install.type = "PACKAGE"; | ||
27 | }; | ||
28 | }; | ||
29 | myServices.chatonsProperties.services.nextcloud = { | ||
30 | file.datetime = "2022-08-21T19:50:00"; | ||
31 | service = { | ||
32 | name = "Nextcloud"; | ||
33 | description = "The self-hosted productivity platform that keeps you in control"; | ||
34 | website = "https://cloud.immae.eu/"; | ||
35 | logo = "https://cloud.immae.eu/core/img/favicon.ico"; | ||
36 | status.level = "OK"; | ||
37 | status.description = "OK"; | ||
38 | registration."" = ["MEMBER" "CLIENT"]; | ||
39 | registration.load = "OPEN"; | ||
40 | install.type = "PACKAGE"; | ||
41 | guide.user = "https://www.immae.eu/docs/nextcloud.html"; | ||
42 | }; | ||
43 | software = { | ||
44 | name = "Nextcloud"; | ||
45 | website = "https://nextcloud.com/"; | ||
46 | license.url = "https://github.com/nextcloud/server/blob/master/COPYING"; | ||
47 | license.name = "GNU Affero General Public License v3.0"; | ||
48 | version = ncfg.rootDir.version; | ||
49 | source.url = "https://github.com/nextcloud/server"; | ||
50 | modules = map (a: a.appName) ncfg.rootDir.apps; | ||
51 | }; | ||
52 | }; | ||
53 | |||
54 | myServices.tools.cloud.farm.instances.immae = { | ||
55 | nextcloud = pkgs.webapps-nextcloud_27.override ({ | ||
56 | # Allow /index.php redirects | ||
57 | postInstall = '' | ||
58 | cd $out | ||
59 | ${pkgs.php81}/bin/php ${./add-htaccess.php} / | ||
60 | ''; | ||
61 | }); | ||
62 | apps = a: [ | ||
63 | a.side_menu a.audioplayer a.bookmarks a.calendar a.carnet a.contacts | ||
64 | a.cookbook a.deck a.extract a.files_markdown a.files_mindmap | ||
65 | a.gpxpod a.keeweb a.maps a.metadata a.music | ||
66 | a.notes a.passman a.polls a.spreed a.tasks | ||
67 | ]; | ||
68 | varDir = "/var/lib/nextcloud"; | ||
69 | secretsPath = "webapps/tools-nextcloud"; | ||
70 | phpPackage = pkgs.php81; | ||
71 | # Be careful when editing that: config from here takes | ||
72 | # precedence over the regular one, but if a key got removed, it my | ||
73 | # still exist in the default config file | ||
74 | config = let | ||
75 | env = config.myEnv.tools.nextcloud; | ||
76 | in { | ||
77 | "dbtype" = "pgsql"; | ||
78 | "dbname" = env.postgresql.database; | ||
79 | "dbhost" = env.postgresql.socket; | ||
80 | "dbport" = ""; | ||
81 | "dbtableprefix" = "oc_"; | ||
82 | "dbuser" = env.postgresql.user; | ||
83 | "dbpassword" = env.postgresql.password; | ||
84 | |||
85 | "instanceid" = env.instance_id; | ||
86 | "passwordsalt" = env.password_salt; | ||
87 | "secret" = env.secret; | ||
88 | |||
89 | "trusted_domains" = [ "cloud.immae.eu" ]; | ||
90 | "overwrite.cli.url" = "https://cloud.immae.eu"; | ||
91 | |||
92 | "lost_password_link" = "disabled"; | ||
93 | |||
94 | "remember_login_cookie_lifetime" = 60*60*24*30; | ||
95 | "session_keepalive" = true; | ||
96 | "session_lifefime" = 60*60*24*30; | ||
97 | |||
98 | "maxZipInputSize" = 0; | ||
99 | "allowZipDownload" = true; | ||
100 | |||
101 | # set by Carnet | ||
102 | "has_rebuilt_cache" = true; | ||
103 | |||
104 | "memcache.distributed" = "\\OC\\Memcache\\Redis"; | ||
105 | "memcache.locking" = "\\OC\\Memcache\\Redis"; | ||
106 | "filelocking.enabled" = true; | ||
107 | "redis" = { | ||
108 | "host" = env.redis.socket; | ||
109 | "port" = 0; | ||
110 | "dbindex" = env.redis.db; | ||
111 | }; | ||
112 | |||
113 | "ldapIgnoreNamingRules" = false; | ||
114 | "ldapProviderFactory" = "\\OCA\\User_LDAP\\LDAPProviderFactory"; | ||
115 | |||
116 | "mail_smtpmode" = "sendmail"; | ||
117 | "mail_smtphost" = "127.0.0.1"; | ||
118 | "mail_smtpname" = ""; | ||
119 | "mail_smtppassword" = ""; | ||
120 | "mail_from_address" = "nextcloud"; | ||
121 | "mail_smtpauth" = false; | ||
122 | "mail_domain" = "tools.immae.eu"; | ||
123 | }; | ||
124 | }; | ||
125 | services.websites.env.tools.modules = [ "proxy_fcgi" ]; | ||
126 | |||
127 | security.acme.certs.eldiron.extraDomainNames = [ "cloud.immae.eu" ]; | ||
128 | services.websites.env.tools.vhostConfs.cloud = { | ||
129 | certName = "eldiron"; | ||
130 | hosts = ["cloud.immae.eu" ]; | ||
131 | root = ncfg.rootDir; | ||
132 | extraConfig = [ | ||
133 | ncfg.vhost | ||
134 | ]; | ||
135 | }; | ||
136 | |||
137 | myServices.monitoring.fromMasterActivatedPlugins = [ "http" ]; | ||
138 | myServices.monitoring.fromMasterObjects.service = [ | ||
139 | { | ||
140 | service_description = "owncloud website is running on cloud.immae.eu"; | ||
141 | host_name = config.hostEnv.fqdn; | ||
142 | use = "external-web-service"; | ||
143 | check_command = ["check_https" "cloud.immae.eu" "/" "a safe home for all your data"]; | ||
144 | |||
145 | servicegroups = "webstatus-webapps"; | ||
146 | _webstatus_name = "Nextcloud"; | ||
147 | _webstatus_url = "https://cloud.immae.eu"; | ||
148 | } | ||
149 | ]; | ||
150 | }; | ||
151 | } | ||
diff --git a/systems/eldiron/websites/cloud/farm.nix b/systems/eldiron/websites/cloud/farm.nix new file mode 100644 index 0000000..df24cba --- /dev/null +++ b/systems/eldiron/websites/cloud/farm.nix | |||
@@ -0,0 +1,221 @@ | |||
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 | } | ||